103
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;
}
Mark Elliot
  • 75,278
  • 22
  • 140
  • 160
avi
  • 1,847
  • 3
  • 16
  • 17

14 Answers14

134

Enumerations in C are numbers that have convenient names inside your code. They are not strings, and the names assigned to them in the source code are not compiled into your program, and so they are not accessible at runtime.

The only way to get what you want is to write a function yourself that translates the enumeration value into a string. E.g. (assuming here that you move the declaration of enum Days outside of 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));

Alternatively, you could use an array as a map, e.g.

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

/* ... */

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

But here you would probably want to assign Sunday = 0 in the enumeration to be safe... I'm not sure if the C standard requires compilers to begin enumerations from 0, although most do (I'm sure someone will comment to confirm or deny this).

Tyler McHenry
  • 74,820
  • 18
  • 121
  • 166
  • I understood.... I Think I will have to do it the other way round only. Thanks Mr Tyler. – avi Jul 02 '10 at 18:51
  • 5
    Aw, you beat me to the array solution. :P But yes, enums always start at 0 unless you specify a different value. – casablanca Jul 02 '10 at 18:51
  • 1
    If I were relying on using the enumerations as indexes, I would actually prefer to explicitly number each one. Unnecessary according to standards, but as a group compilers have not exactly been the best at following standards in my experience. – jdmichal Jul 02 '10 at 18:51
  • 3
    The C standard says, "If the first enumerator has no =, the value of its enumeration constant is 0". But it doesn't hurt anything to have it explicit. – Michael Burr Jul 02 '10 at 18:52
  • 22
    Don't forget that with C99 you can do `const char* dayNames[] = {[Sunday] = "Sunday", [Monday] = "Monday", [Tuesday] = "Tuesday", /* ... etc ... */ };`. You know, in case the days of the week are reordered, or you decide that Monday is the first day of the week. – Tim Schaeffer Jul 02 '10 at 20:13
  • You can print `#Monday`, I'm not sure why this answer is upvoted so much. – user3467349 Jun 07 '15 at 14:19
  • 3
    @user3467349 That (preprocessor stringification) just turns the symbol following the # into a string. So, yes, #Monday would turn into "Monday" but `Days TheDay = Monday; printf("%s", #TheDay);` would print "TheDay". – Tyler McHenry Jun 07 '15 at 20:31
  • enum { i = 1, j , k,l,m,lm }; if (2 == j) { printf("True\n"); } else { printf("else\n"); } this code gives output as True, So how can u say that members of enum and the names assigned to their values are not getting compiled ?? Here how the compiler knows that "j" has value 2?? – msc May 09 '16 at 06:39
  • @Rxmsc The compiler certainly is aware of the association between the symbol j and the number 2 while compiling your code from its C source to the machine code that will ultimately run, but the resulting machine code does not contain any mapping from the character j to the integer 2 that your program can make use of at runtime (e.g. for printing). In the final machine code, the instructions that correspond to "if (2 == j)" are comparing the literal integer 2 to the contents of some memory address or register that the compiler chose to hold the value of the variable you called `j`. – Tyler McHenry May 20 '16 at 02:27
  • What about "break" for each case? – luismartingil Dec 07 '18 at 09:50
  • "What about "break" for each case?" – `break` would be unreachable since after each case the function returns right away. – Daniel Feb 01 '19 at 16:26
  • @TylerMcHenry That would turn into a syntax error. `#` is only valid within macros. – S.S. Anne Sep 28 '19 at 20:25
37

I use something like this:

in a file "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

then in any header file you make the enum declaration, day 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)

then in a file called EnumToString.c:

#include "enum.h"

#define GENERATE_ENUM_STRINGS  // Start string generation

#include "enum.h"             

#undef GENERATE_ENUM_STRINGS   // Stop string generation

then in 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;
}

this will generate "automatically" the strings for any enums declared this way and included in "EnumToString.c"

Vargas
  • 2,125
  • 2
  • 33
  • 53
  • 6
    It's ugly to read through, but you don't have data duplication. (Unlike everyone else.) I'm torn on whether to like this. – Kim Reece Jul 02 '10 at 19:25
  • 2
    +1 for the awesomely creative solution with no data duplication and probably the best maintainability/flexibility, but yech! I think I'd still rather just go the const char*[] route. – manifest Jul 02 '10 at 21:00
  • 4
    Yeah, the maintainability is awesome! It's really easy to update when the days of the week change! By the way, this isn't even useful for localization purposes since the mapping between the English strings and the names in the program is now hard-coded by your attempt to avoid duplication. At least with the other approaches, it's possible to translate the strings without changing every occurrence in the source files. – R.. GitHub STOP HELPING ICE Jul 03 '10 at 05:20
  • 1
    You can probably internationalize it by (with something like gettext) changing the return statements to `return _(#element)` and the like. – Vargas Jul 05 '10 at 12:09
  • 1
    When the C preprocessor is this useful but this ugly, I usually replace it with a simple code generator or custom preprocessor in a scripting language. And in fact, I've got a Python script I've used for exactly this purpose in multiple projects. But I don't use it that often nowadays—for many use cases you can get away with just using strings and not bothering with the enums (and even more so in C++ or ObjC). – abarnert May 11 '12 at 00:50
  • 1
    Why does everybody complain about data duplication, yet have no problem with function prototypes? – Braden Best Sep 24 '15 at 03:07
7

The way I usually do this is by storing the string representations in a separate array in the same order, then indexing the array with the enum value:

const char *DayNames[] = { "Sunday", "Monday", "Tuesday", /* etc */ };
printf("%s", DayNames[Sunday]); // prints "Sunday"
casablanca
  • 69,683
  • 7
  • 133
  • 150
5

enums in C don't really work the way you're expecting them to. You can think of them kind of like glorified constants (with a few additional benefits relating to being a collection of such constants), and the text you've written in for "Sunday" really gets resolved to a number during compilation, the text is ultimately discarded.

In short: to do what you really want you'll need to keep an array of the strings or create a function to map from the enum's value to the text you'd like to print.

Mark Elliot
  • 75,278
  • 22
  • 140
  • 160
4

Enumerations in C are basically syntactical sugar for named lists of automatically-sequenced integer values. That is, when you have this code:

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

    Days TheDay = Monday;
}

Your compiler actually spits out this:

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

Therefore, outputting a C enumeration as a string is not an operation that makes sense to the compiler. If you want to have human-readable strings for these, you will need to define functions to convert from enumerations to strings.

jdmichal
  • 10,984
  • 4
  • 43
  • 42
4

Here's a cleaner way to do it with 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;
}

I'm not sure that this is totally portable b/w preprocessors, but it works with gcc.

This is c99 btw, so use c99 strict if you plug it into (the online compiler) ideone.

Tim Schaeffer
  • 2,616
  • 1
  • 16
  • 20
4

I know I am late to the party, but how about this?

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

This way, you do not have to manually keep the enum and the char* array in sync. If you are like me, chances are that you will later change the enum, and the char* array will print invalid strings. This may not be a feature universally supported. But afaik, most of the mordern day C compilers support this designated initialier style.

You can read more about designated initializers here.

fortytwo
  • 491
  • 1
  • 5
  • 16
3

I like this to have enum in the dayNames. To reduce typing, we can do the following:

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

const char* dayNames[] = { EP(Sunday), EP(Monday)};
Icarus
  • 1,627
  • 7
  • 18
  • 32
Heng lou
  • 31
  • 1
1

The question is you want write the name just one times.
I have an ider like this:

#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";
    }

it uses a struct to insert enum, so that a printer to string could follows each enum value define.

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

The difference to enum is builder can not report error if the numbers are repeated. if you don't like write number, __LINE__ could replace it:

#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

1

There is another solution: Create your own dynamic enumeration class. Means you have a struct and some function to create a new enumeration, which stores the elements in a struct and each element has a string for the name. You also need some type to store a individual elements, functions to compare them and so on. Here is an example:

#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 *parent;
  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->parent==b->parent && 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;
}

The functions of enumeration should be in their own translation unit, but i combined them here to make it simpler.

The advantage is that this solution is flexible, follows the DRY principle, you can store information along with each element, you can create new enumerations during runtime and you can add new elements during runtime. The disadvantage is that this is complex, needs dynamic memory allocation, can't be used in switch-case, needs more memory and is slower. The question is if you should not use a higher level language in cases where you need this.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
1

Using a Macro and stringize operator(#) we can achieve this....

#include <stdio.h>
typedef enum
{
    MON=0,
    TUE
}week;
int main()
{
    #define printt(data)        printf("%s",#data);
    printt(MON);
    return 0;
}
  • This only works if you directly print MON, if you print 0, or the enum value is in a variable called 'x', it will display 0 or x. – braindigitalis Apr 18 '23 at 17:27
0

i'm new to this but a switch statement will defenitely work

#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;
}
Suraj K Thomas
  • 5,773
  • 4
  • 52
  • 64
0

From Using the TRACE_EVENT() macro (Part 3)

#define DOGS { C(JACK_RUSSELL), C(BULL_TERRIER), C(ITALIAN_GREYHOUND) }
#undef C
#define C(a) ENUM_##a
enum dog_enums DOGS;
#undef C
#define C(a) #a
char *dog_strings[] = DOGS;
char *dog_to_string(enum dog_enums dog)
{
    return dog_strings[dog];
}
user16217248
  • 3,119
  • 19
  • 19
  • 37
mageek
  • 13
  • 3
-3

TheDay maps back to some integer type. So:

printf("%s", TheDay);

Attempts to parse TheDay as a string, and will either print out garbage or crash.

printf is not typesafe and trusts you to pass the right value to it. To print out the name of the value, you'd need to create some method for mapping the enum value to a string - either a lookup table, giant switch statement, etc.

Michael
  • 54,279
  • 5
  • 125
  • 144