Is there a possibility to convert enumerator names to string in C?
10 Answers
One way, making the preprocessor do the work. It also ensures your enums and strings are in sync.
#define FOREACH_FRUIT(FRUIT) \
FRUIT(apple) \
FRUIT(orange) \
FRUIT(grape) \
FRUIT(banana) \
#define GENERATE_ENUM(ENUM) ENUM,
#define GENERATE_STRING(STRING) #STRING,
enum FRUIT_ENUM {
FOREACH_FRUIT(GENERATE_ENUM)
};
static const char *FRUIT_STRING[] = {
FOREACH_FRUIT(GENERATE_STRING)
};
After the preprocessor gets done, you'll have:
enum FRUIT_ENUM {
apple, orange, grape, banana,
};
static const char *FRUIT_STRING[] = {
"apple", "orange", "grape", "banana",
};
Then you could do something like:
printf("enum apple as a string: %s\n",FRUIT_STRING[apple]);
If the use case is literally just printing the enum name, add the following macros:
#define str(x) #x
#define xstr(x) str(x)
Then do:
printf("enum apple as a string: %s\n", xstr(apple));
In this case, it may seem like the two-level macro is superfluous, however, due to how stringification works in C, it is necessary in some cases. For example, let's say we want to use a #define with an enum:
#define foo apple
int main() {
printf("%s\n", str(foo));
printf("%s\n", xstr(foo));
}
The output would be:
foo
apple
This is because str will stringify the input foo rather than expand it to be apple. By using xstr the macro expansion is done first, then that result is stringified.
See Stringification for more information.

- 2,791
- 1
- 14
- 13
-
Very simple as you described. I just want enum to string and string to enum for an enum type in a header file. I don't want to create an implementation file (for C++/Objective-C) when dealing with enums. – p0lAris Apr 29 '15 at 14:53
-
You could iterate FRUIT_STRING array and do a string comparison. If a match is found, then the index is the value of the ENUM, assuming non-sparse enums. – Terrence M Apr 29 '15 at 15:37
-
9If you do not want to pollute the namespace with apple and orange... you can prefix it with `#define GENERATE_ENUM(ENUM) PREFIX##ENUM,` – jsaak Jul 05 '16 at 18:26
-
this approach has the disadvantage that it is not easy to find where an enum item was defined. – brita_ Jul 12 '16 at 12:48
-
The last line containing str(apple) will not work when if apple is an int value rather than the literal text "apple". Instead if a variable called i were passed it would print "enum apple as a string: i". Which makes sense because this works at the preprocessor level. – hookenz Feb 08 '17 at 21:05
-
Thank you for this awesome answer. Rather than using this exact solution (for a slightly different problem), it gave me the idea to use macros with some extra parameters to generate IF statements [if (value.equals(F(STRING))) then index = CODE;] - this saved me tons of memory! – Dan Mar 11 '17 at 02:09
-
6For those who come across this post, this method of using a macro list to enumerate various items in a program is informally called "X macros". – Lundin Sep 03 '18 at 06:59
In a situation where you have this:
enum fruit {
apple,
orange,
grape,
banana,
// etc.
};
I like to put this in the header file where the enum is defined:
static inline char *stringFromFruit(enum fruit f)
{
static const char *strings[] = { "apple", "orange", "grape", "banana", /* continue for rest of values */ };
return strings[f];
}

- 55,009
- 24
- 135
- 201
-
4For the life of me I can't see how this helps. Could you expand a little to make it more obvious. – David Heffernan Mar 28 '12 at 12:22
-
3OK, how does that help? Are you saying it is easier to type `enumToString(apple)` than to type `"apple"`? It's not like there is any type-safety anywhere. Unless I am missing something what you suggest here is pointless and just succeeds in obfuscating code. – David Heffernan Mar 28 '12 at 12:32
-
@DavidHeffernan and that's where the function comes in. You will be returned an invalid string if you invoke `stringFromFruit()` with an invalid value, the macro is simply there because that's what I like to use. Nothing more. I can understand that the OP wouldn't consider it practical, thats what the second answer is for. – Richard J. Ross III Mar 28 '12 at 12:33
-
2OK, I see now. The macro is bogus in my view and I suggest you delete it. – David Heffernan Mar 28 '12 at 12:41
-
Yes, thats easy. But I've got already a very long enum struct and i need to print their names instead of an int. – Misha Mar 28 '12 at 12:56
-
@AdilSoomro why did you edit my code? This isn't C#, enums are expected to have lower-case names... – Richard J. Ross III Jul 06 '12 at 11:42
-
@RichardJ.RossIII: sorry for that, I actually was looking for solution in Objective C. so I edited it in Objective C, just gave it a CamelCase. sorry again. :) – Adil Soomro Jul 06 '12 at 11:46
-
2
-
@Manty you can see it in the [revision history](http://stackoverflow.com/revisions/9907283/1), but it's not super useful for most cases, so I removed it. – Richard J. Ross III Apr 04 '14 at 07:02
-
1I've often needed an int-to-string conversion when I have just the int and need to print debugging information. Although it may be easier to type "apple" than enumToString(apple), that's not the use case. In my case I have an embedded device that returns ints, and the host side needs to print out the human-readable value. – Sonicsmooth Jul 07 '14 at 19:25
-
4This is also inconvenient to maintain. If I insert a new enum I have to rememeber to duplicate that also in the array, in the correct position. – Fabio Jun 22 '17 at 07:01
-
1The return would discard the const qualifier introduced by the array definition. Thus you should remove it there or add it to the function signature, eg: `static inline const char *stringFromFruit(enum fruit f)`. – Phidelux May 18 '18 at 16:48
You don't need to rely on the preprocessor to ensure your enums and strings are in sync. To me using macros tend to make the code harder to read.
Using Enum And An Array Of Strings
enum fruit
{
APPLE = 0,
ORANGE,
GRAPE,
BANANA,
/* etc. */
FRUIT_MAX
};
const char * const fruit_str[] =
{
[BANANA] = "banana",
[ORANGE] = "orange",
[GRAPE] = "grape",
[APPLE] = "apple",
/* etc. */
};
Note: the strings in the fruit_str
array don't have to be declared in the same order as the enum items.
How To Use It
printf("enum apple as a string: %s\n", fruit_str[APPLE]);
Adding A Compile Time Check
If you are afraid to forget one string, you can add the following check:
#define ASSERT_ENUM_TO_STR(sarray, max) \
typedef char assert_sizeof_##max[(sizeof(sarray)/sizeof(sarray[0]) == (max)) ? 1 : -1]
ASSERT_ENUM_TO_STR(fruit_str, FRUIT_MAX);
An error would be reported at compile time if the amount of enum items does not match the amount of strings in the array.

- 2,021
- 15
- 22
-
Cool! You can use this one instead of ASSERT_ENUM_TO_STR macro, with #include
: static_assert((sizeof(fruit_str)/sizeof(fruit_str[0]) == (FRUIT_MAX)), "fruit enum has different elements than fruit_str[]"); – dAm2K Apr 29 '22 at 11:51 -
this is terrible solution requiring multiple changes each time you change enum. – Enerccio Aug 05 '22 at 12:36
-
I found a C preprocessor trick that is doing the same job without declaring a dedicated array string (Source: http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/c_preprocessor_applications_en).
Sequential enums
Following the invention of Stefan Ram, sequential enums (without explicitely stating the index, e.g. enum {foo=-1, foo1 = 1}
) can be realized like this genius trick:
#include <stdio.h>
#define NAMES C(RED)C(GREEN)C(BLUE)
#define C(x) x,
enum color { NAMES TOP };
#undef C
#define C(x) #x,
const char * const color_name[] = { NAMES };
This gives the following result:
int main( void ) {
printf( "The color is %s.\n", color_name[ RED ]);
printf( "There are %d colors.\n", TOP );
}
The color is RED.
There are 3 colors.
Non-Sequential enums
Since I wanted to map error codes definitions to are array string, so that I can append the raw error definition to the error code (e.g. "The error is 3 (LC_FT_DEVICE_NOT_OPENED)."
), I extended the code in that way that you can easily determine the required index for the respective enum values:
#define LOOPN(n,a) LOOP##n(a)
#define LOOPF ,
#define LOOP2(a) a LOOPF a LOOPF
#define LOOP3(a) a LOOPF a LOOPF a LOOPF
#define LOOP4(a) a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP5(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP6(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP7(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP8(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF
#define LOOP9(a) a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF a LOOPF
#define LC_ERRORS_NAMES \
Cn(LC_RESPONSE_PLUGIN_OK, -10) \
Cw(8) \
Cn(LC_RESPONSE_GENERIC_ERROR, -1) \
Cn(LC_FT_OK, 0) \
Ci(LC_FT_INVALID_HANDLE) \
Ci(LC_FT_DEVICE_NOT_FOUND) \
Ci(LC_FT_DEVICE_NOT_OPENED) \
Ci(LC_FT_IO_ERROR) \
Ci(LC_FT_INSUFFICIENT_RESOURCES) \
Ci(LC_FT_INVALID_PARAMETER) \
Ci(LC_FT_INVALID_BAUD_RATE) \
Ci(LC_FT_DEVICE_NOT_OPENED_FOR_ERASE) \
Ci(LC_FT_DEVICE_NOT_OPENED_FOR_WRITE) \
Ci(LC_FT_FAILED_TO_WRITE_DEVICE) \
Ci(LC_FT_EEPROM_READ_FAILED) \
Ci(LC_FT_EEPROM_WRITE_FAILED) \
Ci(LC_FT_EEPROM_ERASE_FAILED) \
Ci(LC_FT_EEPROM_NOT_PRESENT) \
Ci(LC_FT_EEPROM_NOT_PROGRAMMED) \
Ci(LC_FT_INVALID_ARGS) \
Ci(LC_FT_NOT_SUPPORTED) \
Ci(LC_FT_OTHER_ERROR) \
Ci(LC_FT_DEVICE_LIST_NOT_READY)
#define Cn(x,y) x=y,
#define Ci(x) x,
#define Cw(x)
enum LC_errors { LC_ERRORS_NAMES TOP };
#undef Cn
#undef Ci
#undef Cw
#define Cn(x,y) #x,
#define Ci(x) #x,
#define Cw(x) LOOPN(x,"")
static const char* __LC_errors__strings[] = { LC_ERRORS_NAMES };
static const char** LC_errors__strings = &__LC_errors__strings[10];
In this example, the C preprocessor will generate the following code:
enum LC_errors { LC_RESPONSE_PLUGIN_OK=-10, LC_RESPONSE_GENERIC_ERROR=-1, LC_FT_OK=0, LC_FT_INVALID_HANDLE, LC_FT_DEVICE_NOT_FOUND, LC_FT_DEVICE_NOT_OPENED, LC_FT_IO_ERROR, LC_FT_INSUFFICIENT_RESOURCES, LC_FT_INVALID_PARAMETER, LC_FT_INVALID_BAUD_RATE, LC_FT_DEVICE_NOT_OPENED_FOR_ERASE, LC_FT_DEVICE_NOT_OPENED_FOR_WRITE, LC_FT_FAILED_TO_WRITE_DEVICE, LC_FT_EEPROM_READ_FAILED, LC_FT_EEPROM_WRITE_FAILED, LC_FT_EEPROM_ERASE_FAILED, LC_FT_EEPROM_NOT_PRESENT, LC_FT_EEPROM_NOT_PROGRAMMED, LC_FT_INVALID_ARGS, LC_FT_NOT_SUPPORTED, LC_FT_OTHER_ERROR, LC_FT_DEVICE_LIST_NOT_READY, TOP };
static const char* __LC_errors__strings[] = { "LC_RESPONSE_PLUGIN_OK", "" , "" , "" , "" , "" , "" , "" , "" "LC_RESPONSE_GENERIC_ERROR", "LC_FT_OK", "LC_FT_INVALID_HANDLE", "LC_FT_DEVICE_NOT_FOUND", "LC_FT_DEVICE_NOT_OPENED", "LC_FT_IO_ERROR", "LC_FT_INSUFFICIENT_RESOURCES", "LC_FT_INVALID_PARAMETER", "LC_FT_INVALID_BAUD_RATE", "LC_FT_DEVICE_NOT_OPENED_FOR_ERASE", "LC_FT_DEVICE_NOT_OPENED_FOR_WRITE", "LC_FT_FAILED_TO_WRITE_DEVICE", "LC_FT_EEPROM_READ_FAILED", "LC_FT_EEPROM_WRITE_FAILED", "LC_FT_EEPROM_ERASE_FAILED", "LC_FT_EEPROM_NOT_PRESENT", "LC_FT_EEPROM_NOT_PROGRAMMED", "LC_FT_INVALID_ARGS", "LC_FT_NOT_SUPPORTED", "LC_FT_OTHER_ERROR", "LC_FT_DEVICE_LIST_NOT_READY", };
This results to the following implementation capabilities:
LC_errors__strings[-1] ==> LC_errors__strings[LC_RESPONSE_GENERIC_ERROR] ==> "LC_RESPONSE_GENERIC_ERROR"

- 755
- 7
- 30
-
1Nice. This is exactly what I was looking and using it for. Same errors :) – mrbean Mar 20 '20 at 02:19
-
1
-
@Michael: Good point. Even though it makes it harder to maintain, it produces shorter code – Maschina Jul 05 '21 at 10:23
-
Perfect. Don't forget to define `color_name` as static if it is in a header file. – DimP Sep 06 '22 at 13:42
There is no simple way to achieves this directly. But P99 has macros that allow you to create such type of function automatically:
P99_DECLARE_ENUM(color, red, green, blue);
in a header file, and
P99_DEFINE_ENUM(color);
in one compilation unit (.c file) should then do the trick, in that example the function then would be called color_getname
.

- 76,821
- 6
- 102
- 177
An simpler alternative to Hokyo's "Non-Sequential enums" answer, based on using designators to instantiate the string array:
#define NAMES C(RED, 10)C(GREEN, 20)C(BLUE, 30)
#define C(k, v) k = v,
enum color { NAMES };
#undef C
#define C(k, v) [v] = #k,
const char * const color_name[] = { NAMES };

- 53
- 4
-
1This won't work when the index is negative (quite common in error enums) – DadiBit Apr 24 '21 at 15:11
A function like that without validating the enum is a trifle dangerous. I suggest using a switch statement. Another advantage is that this can be used for enums that have defined values, for example for flags where the values are 1,2,4,8,16 etc.
Also put all your enum strings together in one array:-
static const char * allEnums[] = {
"Undefined",
"apple",
"orange"
/* etc */
};
define the indices in a header file:-
#define ID_undefined 0
#define ID_fruit_apple 1
#define ID_fruit_orange 2
/* etc */
Doing this makes it easier to produce different versions, for example if you want to make international versions of your program with other languages.
Using a macro, also in the header file:-
#define CASE(type,val) case val: index = ID_##type##_##val; break;
Make a function with a switch statement, this should return a const char *
because the strings static consts:-
const char * FruitString(enum fruit e){
unsigned int index;
switch(e){
CASE(fruit, apple)
CASE(fruit, orange)
CASE(fruit, banana)
/* etc */
default: index = ID_undefined;
}
return allEnums[index];
}
If programming with Windows then the ID_ values can be resource values.
(If using C++ then all the functions can have the same name.
string EnumToString(fruit e);
)

- 2,997
- 21
- 20
I usually do this:
#define COLOR_STR(color) \
(RED == color ? "red" : \
(BLUE == color ? "blue" : \
(GREEN == color ? "green" : \
(YELLOW == color ? "yellow" : "unknown"))))

- 171
- 1
- 7
-
This is not a bad answer. It is clear, simple and easy to understand. If you are working on systems where other people need to read and understand your code quickly, clarity is very important. I would not recommend using preprocessor tricks unless they are thoroughly commented or described in a coding standard. – nielsen Sep 04 '19 at 07:32
I settled on making a function whose body is updated by copying the enum over and using a regex in Vim. I use a switch-case because my enum isn't compact so we have maximum flexibility. I keep the regex as a comment in the code so it's just a matter of copy-pasting it.
My enum (shortened, the real one is much bigger):
enum opcode
{
op_1word_ops = 1024,
op_end,
op_2word_ops = 2048,
op_ret_v,
op_jmp,
op_3word_ops = 3072,
op_load_v,
op_load_i,
op_5word_ops = 5120,
op_func2_vvv,
};
The function before copying the enum over:
const char *get_op_name(enum opcode op)
{
// To update copy the enum and apply this regex:
// s/\t\([^, ]*\).*$/\t\tcase \1: \treturn "\1";
switch (op)
{
}
return "Unknown op";
}
I paste the contents of the enum inside the switch brackets:
const char *get_op_name(enum opcode op)
{
// To update copy the enum and apply this regex:
// s/\t\([^, ]*\).*$/\t\tcase \1: \treturn "\1";
switch (op)
{
op_1word_ops = 1024,
op_end,
op_2word_ops = 2048,
op_ret_v,
op_jmp,
op_3word_ops = 3072,
op_load_v,
op_load_i,
op_5word_ops = 5120,
op_func2_vvv,
}
return "Unknown op";
}
Then with Shift-V in Vim I select the lines, press :
then paste (Ctrl-V on Windows) the regex s/\t\([^, ]*\).*$/\t\tcase \1: \treturn "\1";
and press Enter:
const char *get_op_name(enum opcode op)
{
// To update copy the enum and apply this regex:
// s/\t\([^, ]*\).*$/\t\tcase \1: \treturn "\1";
switch (op)
{
case op_1word_ops: return "op_1word_ops";
case op_end: return "op_end";
case op_2word_ops: return "op_2word_ops";
case op_ret_v: return "op_ret_v";
case op_jmp: return "op_jmp";
case op_3word_ops: return "op_3word_ops";
case op_load_v: return "op_load_v";
case op_load_i: return "op_load_i";
case op_5word_ops: return "op_5word_ops";
case op_func2_vvv: return "op_func2_vvv";
}
return "Unknown op";
}
The regex skips the first \t
character, then puts every character that follows that is neither ,
nor
into \1
and matches the rest of the line to delete everything. Then with \1
being the enum label it remakes the lines in the case <label>: return "<label>";
format. Note that it looks poorly aligned in this post only because StackOverflow uses 4-space tabulation whereas in Vim I use 8-space tabulation, so you might want to edit the regex for style.

- 1,013
- 1
- 9
- 22
In order to avoid confusion between integers and names of enum members I decided to use, in my debugging process, the next command:
enum {CC, CV, STOP, DESCONECTADO};
unsigned char mode;
Serial.print(" mode = "); Serial.println((mode == 0) ? "CC" : (mode == 1) ? "CV" : (mode == 2) ? "STOP" : "DESCONECTADO");
This way it prints the word of the state instead of the integer value.

- 674
- 1
- 5
- 10