Imprimir texto en lugar de valor de C enum


87
int main()
{

  enum Days{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday};

  Days TheDay;

  int j = 0;

  printf("Please enter the day of the week (0 to 6)\n");

  scanf("%d",&j);

  TheDay = Days(j);

  //how to PRINT THE VALUES stored in TheDay

  printf("%s",TheDay);  //   isnt working

  return 0;
}

¿Su salida esperada es que imprima la cadena "Domingo", etc.?
GalacticCowboy

Respuestas:


104

Las enumeraciones en C son números que tienen nombres convenientes dentro de su código. No son cadenas y los nombres que se les asignan en el código fuente no se compilan en su programa, por lo que no son accesibles en tiempo de ejecución.

La única forma de obtener lo que desea es escribir usted mismo una función que traduzca el valor de la enumeración en una cadena. Por ejemplo (asumiendo aquí que mueves la declaración de enum Daysfuera de main):

const char* getDayName(enum Days day) 
{
   switch (day) 
   {
      case Sunday: return "Sunday";
      case Monday: return "Monday";
      /* etc... */
   }
}

/* Then, later in main: */
printf("%s", getDayName(TheDay));

Alternativamente, puede usar una matriz como mapa, por ejemplo

const char* dayNames[] = {"Sunday", "Monday", "Tuesday", /* ... etc ... */ };

/* ... */

printf("%s", dayNames[TheDay]);

Pero aquí probablemente desee asignar Sunday = 0en la enumeración para estar seguro ... No estoy seguro de si el estándar C requiere que los compiladores comiencen las enumeraciones desde 0, aunque la mayoría lo hace (estoy seguro de que alguien comentará para confirmar o negar esto ).


3
Aw, me ganaste con la solución de matriz. : P Pero sí, las enumeraciones siempre comienzan en 0 a menos que especifique un valor diferente.
casablanca

1
Si confiara en usar las enumeraciones como índices, en realidad preferiría numerar explícitamente cada uno. Innecesario según los estándares, pero como grupo, los compiladores no han sido exactamente los mejores en seguir los estándares en mi experiencia.
jdmichal

3
El estándar C dice: "Si el primer enumerador no tiene =, el valor de su constante de enumeración es 0". Pero no está de más tenerlo explícito.
Michael Burr

17
No olvide que con C99 puede hacerlo const char* dayNames[] = {[Sunday] = "Sunday", [Monday] = "Monday", [Tuesday] = "Tuesday", /* ... etc ... */ };. Ya sabes, en caso de que se reordenen los días de la semana, o decidas que el lunes es el primer día de la semana.
Tim Schaeffer

2
@ user3467349 Eso (cadena de preprocesador) simplemente convierte el símbolo que sigue al # en una cadena. Entonces, sí, #Monday se convertiría en "Monday" pero Days TheDay = Monday; printf("%s", #TheDay);imprimiría "TheDay".
Tyler McHenry

29

Yo uso algo como esto:

en un archivo "EnumToString.h":

#undef DECL_ENUM_ELEMENT
#undef DECL_ENUM_ELEMENT_VAL
#undef DECL_ENUM_ELEMENT_STR
#undef DECL_ENUM_ELEMENT_VAL_STR
#undef BEGIN_ENUM
#undef END_ENUM

#ifndef GENERATE_ENUM_STRINGS
    #define DECL_ENUM_ELEMENT( element ) element,
    #define DECL_ENUM_ELEMENT_VAL( element, value ) element = value,
    #define DECL_ENUM_ELEMENT_STR( element, descr ) DECL_ENUM_ELEMENT( element )
    #define DECL_ENUM_ELEMENT_VAL_STR( element, value, descr ) DECL_ENUM_ELEMENT_VAL( element, value )
    #define BEGIN_ENUM( ENUM_NAME ) typedef enum tag##ENUM_NAME
    #define END_ENUM( ENUM_NAME ) ENUM_NAME; \
            const char* GetString##ENUM_NAME(enum tag##ENUM_NAME index);
#else
    #define BEGIN_ENUM( ENUM_NAME) const char * GetString##ENUM_NAME( enum tag##ENUM_NAME index ) {\
        switch( index ) { 
    #define DECL_ENUM_ELEMENT( element ) case element: return #element; break;
    #define DECL_ENUM_ELEMENT_VAL( element, value ) DECL_ENUM_ELEMENT( element )
    #define DECL_ENUM_ELEMENT_STR( element, descr ) case element: return descr; break;
    #define DECL_ENUM_ELEMENT_VAL_STR( element, value, descr ) DECL_ENUM_ELEMENT_STR( element, descr )

    #define END_ENUM( ENUM_NAME ) default: return "Unknown value"; } } ;

#endif

luego, en cualquier archivo de encabezado, realiza la declaración de enumeración, día enum.h

#include "EnumToString.h"

BEGIN_ENUM(Days)
{
    DECL_ENUM_ELEMENT(Sunday) //will render "Sunday"
    DECL_ENUM_ELEMENT(Monday) //will render "Monday"
    DECL_ENUM_ELEMENT_STR(Tuesday, "Tuesday string") //will render "Tuesday string"
    DECL_ENUM_ELEMENT(Wednesday) //will render "Wednesday"
    DECL_ENUM_ELEMENT_VAL_STR(Thursday, 500, "Thursday string") // will render "Thursday string" and the enum will have 500 as value
    /* ... and so on */
}
END_ENUM(MyEnum)

luego en un archivo llamado EnumToString.c:

#include "enum.h"

#define GENERATE_ENUM_STRINGS  // Start string generation

#include "enum.h"             

#undef GENERATE_ENUM_STRINGS   // Stop string generation

luego en main.c:

int main(int argc, char* argv[])
{
    Days TheDay = Monday;
    printf( "%d - %s\n", TheDay, GetStringDay(TheDay) ); //will print "1 - Monday"

    TheDay = Thursday;
    printf( "%d - %s\n", TheDay, GetStringDay(TheDay) ); //will print "500 - Thursday string"

    return 0;
}

esto generará "automáticamente" las cadenas para cualquier enumeración declarada de esta manera e incluida en "EnumToString.c"


4
Es feo leerlo, pero no hay duplicación de datos. (A diferencia de todos los demás). No sé si me gusta esto.
Kim Reece

1
+1 para la solución increíblemente creativa sin duplicación de datos y probablemente la mejor capacidad de mantenimiento / flexibilidad, pero ¡vaya! Creo que prefiero seguir la ruta const char * [].
manifiesto el

4
¡Sí, la capacidad de mantenimiento es increíble! ¡Es muy fácil actualizar cuando cambian los días de la semana! </sarcasm> Por cierto, esto ni siquiera es útil para fines de localización, ya que el mapeo entre las cadenas en inglés y los nombres en el programa está ahora codificado por su intento de evitar la duplicación. Al menos con los otros enfoques, es posible traducir las cadenas sin cambiar cada ocurrencia en los archivos fuente.
R .. GitHub DEJA DE AYUDAR A ICE

1
Probablemente pueda internacionalizarlo (con algo como gettext) cambiando las declaraciones de retorno a return _(#element)y similares.
Vargas

Cuando el preprocesador de C es tan útil pero tan feo, generalmente lo reemplazo con un generador de código simple o un preprocesador personalizado en un lenguaje de scripting. Y, de hecho, tengo un script de Python que he usado exactamente para este propósito en múltiples proyectos. Pero no lo uso tan a menudo hoy en día; para muchos casos de uso, puede salirse con la suya usando cadenas y sin molestarse con las enumeraciones (y aún más en C ++ u ObjC).
abarnert

6

La forma en que normalmente hago esto es almacenando las representaciones de cadenas en una matriz separada en el mismo orden, luego indexando la matriz con el valor de enumeración:

const char *DayNames[] = { "Sunday", "Monday", "Tuesday", /* etc */ };
printf("%s", DayNames[Sunday]); // prints "Sunday"

4

enumLos s en C no funcionan realmente de la forma esperada. Puede pensar en ellos como constantes glorificadas (con algunos beneficios adicionales relacionados con ser una colección de tales constantes), y el texto que ha escrito para "Domingo" realmente se resuelve en un número durante la compilación, el texto es finalmente descartado.

En resumen: para hacer lo que realmente desea, deberá mantener una matriz de las cadenas o crear una función para asignar el valor de la enumeración al texto que desea imprimir.


4

Las enumeraciones en C son básicamente azúcar sintáctico para listas nombradas de valores enteros secuenciados automáticamente. Es decir, cuando tienes este código:

int main()
{
    enum Days{Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday};

    Days TheDay = Monday;
}

Su compilador en realidad escupe esto:

int main()
{
    int TheDay = 1; // Monday is the second enumeration, hence 1. Sunday would be 0.
}

Por lo tanto, generar una enumeración de C como una cadena no es una operación que tenga sentido para el compilador. Si desea tener cadenas legibles por humanos para estos, deberá definir funciones para convertir de enumeraciones a cadenas.


4

Aquí hay una forma más limpia de hacerlo con macros:

#include <stdio.h>
#include <stdlib.h>

#define DOW(X, S)                                                         \
    X(Sunday) S X(Monday) S X(Tuesday) S X(Wednesday) S X(Thursday) S X(Friday) S X(Saturday)

#define COMMA ,

/* declare the enum */
#define DOW_ENUM(DOW) DOW
enum dow {
    DOW(DOW_ENUM, COMMA)
};

/* create an array of strings with the enum names... */
#define DOW_ARR(DOW ) [DOW] = #DOW
const char * const dow_str[] = {
    DOW(DOW_ARR, COMMA)
};

/* ...or create a switchy function. */
static const char * dowstr(int i)
{
#define DOW_CASE(D) case D: return #D

    switch(i) {
        DOW(DOW_CASE, ;);
    default: return NULL;
    }
}


int main(void)
{
    for(int i = 0; i < 7; i++)
        printf("[%d] = «%s»\n", i, dow_str[i]);
    printf("\n");
    for(int i = 0; i < 7; i++)
        printf("[%d] = «%s»\n", i, dowstr(i));
    return 0;
}

No estoy seguro de que se trate de preprocesadores b / n totalmente portátiles, pero funciona con gcc.

Esto es c99 por cierto, así c99 strictque úselo si lo conecta a ideone (el compilador en línea) .


Me encanta lo "limpias" que son las macros :-).
mk12

3

Sé que llego tarde a la fiesta, pero ¿qué tal esto?

const char* dayNames[] = { [Sunday] = "Sunday", [Monday] = "Monday", /*and so on*/ };
printf("%s", dayNames[Sunday]); // prints "Sunday"

De esta manera, no es necesario que mantenga sincronizados manualmente enumla char*matriz y la matriz. Si es como yo, lo más probable es que luego cambie enumy la char*matriz imprima cadenas no válidas. Es posible que esta característica no sea compatible universalmente. Pero afaik, la mayoría de los compiladores de C del día de hoy apoyan este estilo inicial designado.

Puede leer más sobre los inicializadores designados aquí .


1

La pregunta es si desea escribir el nombre solo una vez.
Tengo una ider como esta:

#define __ENUM(situation,num) \
    int situation = num;        const char * __##situation##_name = #situation;

    const struct {
        __ENUM(get_other_string, -203);//using a __ENUM Mirco make it ease to write, 
        __ENUM(get_negative_to_unsigned, -204);
        __ENUM(overflow,-205);
//The following two line showing the expanding for __ENUM
        int get_no_num = -201;      const char * __get_no_num_name = "get_no_num";
        int get_float_to_int = -202;        const char * get_float_to_int_name = "float_to_int_name";

    }eRevJson;
#undef __ENUM
    struct sIntCharPtr { int value; const char * p_name; };
//This function transform it to string.
    inline const char * enumRevJsonGetString(int num) {
        sIntCharPtr * ptr = (sIntCharPtr *)(&eRevJson);
        for (int i = 0;i < sizeof(eRevJson) / sizeof(sIntCharPtr);i++) {
            if (ptr[i].value == num) {
                return ptr[i].p_name;
            }
        }
        return "bad_enum_value";
    }

usa una estructura para insertar enumeración, de modo que una impresora a cadena pueda seguir cada valor de enumeración definido.

int main(int argc, char *argv[]) {  
    int enum_test = eRevJson.get_other_string;
    printf("error is %s, number is %d\n", enumRevJsonGetString(enum_test), enum_test);

>error is get_other_string, number is -203

La diferencia con la enumeración es que el constructor no puede informar de errores si los números se repiten. si no le gusta escribir el número, __LINE__podría reemplazarlo:

#define ____LINE__ __LINE__
#define __ENUM(situation) \
    int situation = (____LINE__ - __BASELINE -2);       const char * __##situation##_name = #situation;
constexpr int __BASELINE = __LINE__;
constexpr struct {
    __ENUM(Sunday);
    __ENUM(Monday);
    __ENUM(Tuesday);
    __ENUM(Wednesday);
    __ENUM(Thursday);
    __ENUM(Friday);
    __ENUM(Saturday);
}eDays;
#undef __ENUM
inline const char * enumDaysGetString(int num) {
    sIntCharPtr * ptr = (sIntCharPtr *)(&eDays);
    for (int i = 0;i < sizeof(eDays) / sizeof(sIntCharPtr);i++) {
        if (ptr[i].value == num) {
            return ptr[i].p_name;
        }
    }
    return "bad_enum_value";
}
int main(int argc, char *argv[]) {  
    int d = eDays.Wednesday;
    printf("day %s, number is %d\n", enumDaysGetString(d), d);
    d = 1;
    printf("day %s, number is %d\n", enumDaysGetString(d), d);
}

>day Wednesday, number is 3 >day Monday, number is 1


0

Soy nuevo en esto, pero una declaración de cambio definitivamente funcionará

#include <stdio.h>

enum mycolor;

int main(int argc, const char * argv[])

{
enum Days{Sunday=1,Monday=2,Tuesday=3,Wednesday=4,Thursday=5,Friday=6,Saturday=7};

enum Days TheDay;


printf("Please enter the day of the week (0 to 6)\n");

scanf("%d",&TheDay);

switch (TheDay)
 {

case Sunday:
        printf("the selected day is sunday");
        break;
    case Monday:
        printf("the selected day is monday");
        break;
    case Tuesday:
        printf("the selected day is Tuesday");
        break;
    case Wednesday:
        printf("the selected day is Wednesday");
        break;
    case Thursday:
        printf("the selected day is thursday");
        break;
    case Friday:
        printf("the selected day is friday");
        break;
    case Saturday:
        printf("the selected day is Saturaday");
        break;
    default:
        break;
}

return 0;
}

El formato adecuado (léase: sangría) será imprescindible para el código literal en las respuestas ...
p4010

0

Me gusta que esto tenga enum en los dayNames. Para reducir la escritura, podemos hacer lo siguiente:

#define EP(x) [x] = #x  /* ENUM PRINT */

const char* dayNames[] = { EP(Sunday), EP(Monday)};

0

Hay otra solución: cree su propia clase de enumeración dinámica. Significa que tiene una structfunción y alguna para crear una nueva enumeración, que almacena los elementos en una structy cada elemento tiene una cadena para el nombre. También necesita algún tipo para almacenar elementos individuales, funciones para compararlos, etc. Aquí hay un ejemplo:

#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


struct Enumeration_element_T
{
  size_t index;
  struct Enumeration_T *parrent;
  char *name;
};

struct Enumeration_T
{
  size_t len;
  struct Enumeration_element_T elements[];
};
  


void enumeration_delete(struct Enumeration_T *self)
{
  if(self)
  {
    while(self->len--)
    {
      free(self->elements[self->len].name);
    }
    free(self);
  }
}

struct Enumeration_T *enumeration_create(size_t len,...)
{
  //We do not check for size_t overflows, but we should.
  struct Enumeration_T *self=malloc(sizeof(self)+sizeof(self->elements[0])*len);
  if(!self)
  {
    return NULL;
  }
  self->len=0;
  va_list l; 
  va_start(l,len);
  for(size_t i=0;i<len;i++)
  {
    const char *name=va_arg(l,const char *);
    self->elements[i].name=malloc(strlen(name)+1);
    if(!self->elements[i].name)
    {
      enumeration_delete(self);
      return NULL;
    }
    strcpy(self->elements[i].name,name);
    self->len++;
  }
  return self;
}


bool enumeration_isEqual(struct Enumeration_element_T *a,struct Enumeration_element_T *b)
{
  return a->parrent==b->parrent && a->index==b->index;
}

bool enumeration_isName(struct Enumeration_element_T *a, const char *name)
{
  return !strcmp(a->name,name);
}

const char *enumeration_getName(struct Enumeration_element_T *a)
{
  return a->name;
}

struct Enumeration_element_T *enumeration_getFromName(struct Enumeration_T *self, const char *name)
{
  for(size_t i=0;i<self->len;i++)
  {
    if(enumeration_isName(&self->elements[i],name))
    {
      return &self->elements[i];
    }
  }
  return NULL;
}
  
struct Enumeration_element_T *enumeration_get(struct Enumeration_T *self, size_t index)
{
  return &self->elements[index];
}

size_t enumeration_getCount(struct Enumeration_T *self)
{
  return self->len;
}

bool enumeration_isInRange(struct Enumeration_T *self, size_t index)
{
  return index<self->len;
}



int main(void)
{
  struct Enumeration_T *weekdays=enumeration_create(7,"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday");
  if(!weekdays)
  {
    return 1;
  }
    
  printf("Please enter the day of the week (0 to 6)\n");
  size_t j = 0;
  if(scanf("%zu",&j)!=1)
  {
    enumeration_delete(weekdays);
    return 1;
  }
  // j=j%enumeration_getCount(weekdays); //alternative way to make sure j is in range
  if(!enumeration_isInRange(weekdays,j))
  {
    enumeration_delete(weekdays);
    return 1;
  }

  struct Enumeration_element_T *day=enumeration_get(weekdays,j);
  

  printf("%s\n",enumeration_getName(day));
  
  enumeration_delete(weekdays);

  return 0;
}

Las funciones de enumeración deben estar en su propia unidad de traducción, pero las combiné aquí para hacerlo más simple.

La ventaja es que esta solución es flexible, sigue el principio DRY, puede almacenar información junto con cada elemento, puede crear nuevas enumeraciones durante el tiempo de ejecución y puede agregar nuevos elementos durante el tiempo de ejecución. La desventaja es que es complejo, necesita una asignación de memoria dinámica, no se puede usar en switch- case, necesita más memoria y es más lento. La pregunta es si no debería utilizar un lenguaje de nivel superior en los casos en que lo necesite.


-3

TheDay se asigna de nuevo a algún tipo de entero. Entonces:

printf("%s", TheDay);

Intenta analizar TheDay como una cadena e imprime basura o se bloquea.

printf no es de tipo seguro y confía en que le pasará el valor correcto. Para imprimir el nombre del valor, necesitaría crear algún método para asignar el valor de enumeración a una cadena, ya sea una tabla de búsqueda, una declaración de cambio gigante, etc.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.