This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

enum external declaration and use

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

Parents
  • 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'.

Reply
  • 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'.

Children
  • 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.

    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