Hi Folks, I am trying to use enum to define a set of named constants with values so that they can be used by multiple C files in my project. For example. I use the following snipet to define Months in a headers file externals.h, and then If I then try to use it as follows in any of those files, it does not work.
enum MONTHS {M1 = 'Jan', M2 = 'Feb', M3 = 'Mar'}; .... .... extern enum MONTHS; // should it be extern enum MONTHS months; ? .... ..... SendByte (M1);//(This routine display M1 on my serial port).
I get error ---> (...error 230 MONTH unknown struct/union/enum tag)
What is the problem and how can I fix it ? I would like to get a better understanding of this enum business, if possible..
Thanks.
Bhal Tulpule
An enum is a data type, not a variable. The compiler needs to know the definition of the enum before you can use it, or you'll get an "unknown struct/union/enum" error.
You need to define the full enum in a header file.
It still won't work, though, in your case because enums must be numeric -- you can't use a char array.
Folks, I appreciate your rapid response and help. I forgot that enum is needs to be a integer not a string. Thanks for pointing it out. I think I fixed it and so here is what the files look like: externals.h has the extern stmt.
extern month;
A "main" FILE has the declaration of the enum and some code that uses it...
enum MONTH {M1 = 0x41, M2 = 0x42, M3 = 0x43} month; ...
The program code in that routine (main.c) has some test code to make sure I can manipulate it. This compiles and links ok.
SendByte (M1); month = M1; SendByte (month); month = 0x49; SendByte (month);
However when I try to use the same code above(SendByte...month =... SendByte...) from the main.c file,in another FILE, I get an errors---> M1: undefined identifier.. (that file has include for externals.h) So my whole purpose of defining the set of constants M1, M2, M3 in one place and using them everywhere is not still successful. Maybe I should use some other construct ?
Any help would be appreciated. Thanks. Bhal Tulpule
enum MONTHS {M1 = 'Jan', M2 = 'Feb', M3 = 'Mar'};
Writing a string in single inverted quotes??
Following is also this is valid
typedef enum { Jan = 1, Feb, Mar, ... , Nov, Dec }Months_names;
But dont forget proper declarations.
typedef enum{ test1 = 'AB', test2 = 'BC' }test;
generates:
main.c(17): warning: #2548-D: multicharacter character literal (potential portability problem)
The warning related to Endianess.
I fail to understand, how assigning characters will be useful??
If you have a line of code that fixes a guaranteed byte order, then you could do tag handling like:
switch (fixed_tag(t)) { case 'IMG_': handle_image(); break; case 'MAP_': handle_color_map(); break; case 'DLY_': handle_animation_delay(); break; case 'LAYR': handle_layer(); break; case 'CPYR': handle_copyright_information(); break; case 'ANIM': handle_animation(); break; ... }
If then also making sure file save/load writes the character sequences in readable order, then a hexdump of the file will directly show the tags.
Of course, it would be just as possible to instead make use of:
enum { TAG_IMG_ = xx, TAG_MAP_ = yy, .. }
Following is what i tried and the respective results that i got:
typedef enum{ test1 = 'AB', test2 = 'BC' }test; U32 var1 = test1; U8 var2; U16 var3; var2 = 'BC'; var3 = 'C\0';
Result: var1 = 0x00004142 var2 = 0x00 var3 = 0x4300
can var3 be treated/used as string?
var3 is still not a string. It's an integer.
A C string is accessed using the address of the string.
But you can store data in an integer so that the address of the integer can be used with a string function (after proper type cast).
Next thing is that the character constant 'C\0' _may_ end up in the memory with the 'C' in the lowest byte (same byte as the address of the variable) while the '\0' _may_ be stored in the following byte.
But the memory could just as well store '\0' followed by 'C' meaning a pointer to var3 would typecast as a const char* pointer would then point at a zero-length string.
Your experiments with multi-character constants only looks at the value as an integer, after an assign as a multi-character constant. In real life, you normally have to consider the byte order when a multi-byte integer is written to memory. Basically the result of:
const char *p = (const char*)&var3; printf("first byte: %02x, second byte: %02x.\n",p[0],p[1]);
It is here that architecture really starts to matter.
When just playing with the source code, then a two-character constant 'AB' can be seen as a "base-256" integer with the big digit = 'A' (65) and the small digit = 'B' (66). So 256*'A' + 'B'.
No: the warning relates (as it says) to portability.
"Portability" may include - but is not limited to - endianness...
"I fail to understand, how assigning characters will be useful??"
If you are not concerned about portability, it may be a useful way to pack character codes into an integer value.
So can we use the above code to test Endianess?
It is more obvious what you do, if you skip non-portable multi-character constants and write:
uint16_t endian_val = 0x1234; uint8_t *endian_test = (uint8_t*)&endian_val; if (endian_test[0] == 0x12 && endian_test[1] == 0x34) { printf("Found most significant byte on low address - big-endian machine.\n"); } else if (endian_test[0] = 0x34 && endian_test[1] == 0x12) { printf("Found least significant byte on low address - little-endian machine.\n"); } else { printf("Something weird is going on. Should normally only have two alternatives for a 16-bit variable...\n"); }
But it is way more common to do the reverse - create an array and then view it as an integer:
uint8_t endial_val[4] = {0x12, 0x34, 0x45, 0x67}; if (0x12345678 == *(uint32_t*)&endian_val) { ... } else if (0x78563412 == *(uint32_t*)&endian_val) { ... } else { ... }
Obviously, a four byte large integer would have more options, since it could be little-endian for 16-bit sub-groups but big-endian betwen the two 16-bit sub-groups.
ya, the above methods are the widely known method.
const char *p = (const char*)&var3; printf("first byte: %02x, second byte: %02x.\n",p[0],p[1]); This can be used as a new way around of doing it. also the var3 can be a 4byte long int for more options.
...it could be little-endian for 16-bit sub-groups but big-endian betwen the two 16-bit sub-groups.
do controller have such architectures? May be 16bit controllers?
A controller must obviously work with a word size larger than two bytes, to be able to be partially big-endian and partially little-endian.
The most common is a 16-bit controller that are big or little endian for the native word size. But have dedicated instructions that allows two 16-bit registers to be saved/loaded as one 32-bit value. Like when 16'16 multiply produces a 32-bit result or 32/16 produces a 16-bit result. In that case, the instruction set may write the two registers in the wrong order.
Similar issues is of course possible when looking at 64-bit processors, or 32-bit processors with partial 64-bit support.
"...to be able to be partially big-endian and partially little-endian"
Or it could be implemented in software by the compiler