Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

different size of the same enum type

Tags:

c

enums

embedded

The sizes of enum types in the following code are different, why? Will the declararation of an enum type will cause gcc to treat it as signed int? And this feature cause a problem in my project, for example, the size of one enum type is 1 in "file_1.c", but the size of it is 4 in "file_2.c".

My test code:

enum foo foo_test(void);

enum foo {
    FOO_0
};

enum bar {
    BAR_0
};

int main(int argc, char **argv)
{
    printf("sizeof(enum foo) %d, sizeof(enum bar) %d\n", sizeof(enum foo), sizeof(enum bar));
    return 0;
}
  • When I compile it with arm-none-eabi-gcc v4.9.3 in my embedded project

    • the output is sizeof(enum foo) 4, sizeof(enum bar) 1.
  • When I compile it with gcc v4.8.3 in Windows

    • compile with gcc -Wall -o sizeofTest.exe sizeofTest.c
      • the output is sizeof(enum foo) 4, sizeof(enum bar) 4.
    • compile with gcc -Wall -fshort-enums -o sizeofTest.exe sizeofTest.c
      • the output is sizeof(enum foo) 4, sizeof(enum bar) 1.
like image 549
Ivan Avatar asked Aug 23 '17 11:08

Ivan


People also ask

Can two enums have the same value?

Two enum names can have same value. For example, in the following C program both 'Failed' and 'Freezed' have same value 0.

What is the size of an enumeration type?

An enum type is represented by an underlying integer type. The size of the integer type and whether it is signed is based on the range of values of the enumerated constants. In strict C89 or C99 mode, the compiler allows only enumeration constants with values that will fit in "int" or "unsigned int" (32 bits).

Why is enum size 4?

The size is four bytes because the enum is stored as an int . With only 12 values, you really only need 4 bits, but 32 bit machines process 32 bit quantities more efficiently than smaller quantities.

Does enum have size?

The C standard specifies that enums are integers, but it does not specify the size. Once again, that is up to the people who write the compiler. On an 8-bit processor, enums can be 16-bits wide. On a 32-bit processor they can be 32-bits wide or more or less.


2 Answers

As is so common with the questions asking about peculiar outcomes in c, the reason here is undefined behaviour. The C11 standard draft n1570 6.7.2.3p3:

  1. A type specifier of the form

    enum identifier
    

    without an enumerator list shall only appear after the type it specifies is complete.

And of completeness in C11 6.7.2.2p4:

  1. [...] The enumerated type is incomplete until immediately after the } that terminates the list of enumerator declarations, and complete thereafter.

As a shall in a constraints section was violated, a conforming compiler must output a diagnostics message - however GCC by default isn't a conforming implementation unless you ask it to be -pedantic:

ISO C forbids forward references to ‘enum’ types [-Werror=pedantic]
 enum foo foo_test(void);
      ^

Now, it seems that the compiler there uses the shortest possible enums for any type. Since you used the enum foo before it was actually defined what was in there, the compiler had to resort to int for its type, otherwise char was used. It can be reproduced with -fshort-enums. Your program prints 4 1, whereas this program prints 1 1 with -fshort-enums on my gcc.

#include <stdio.h>

enum foo {
    FOO_0
};

enum foo foo_test(void);    

enum bar {
    BAR_0
};

int main(int argc, char **argv)
{
    printf("sizeof(enum foo) %zu, sizeof(enum bar) %zu\n", 
            sizeof(enum foo), sizeof(enum bar));
    return 0;
}

First of all, fix the undefined behaviour in your code. The correct format specifier for the sizeof expression type is %zu.

The backing type of an enumerator in C can be char, int, or unsigned. The compiler can pick any of these so long as it is capable of storing all the enumerated values.

So a value of 1 is feasible (as sizeof(char) is always 1), and you have asked some of your compilers to squeeze the enum backing types.

The fact that an int appears to be chosen for the backing type for foo is due, I think, to your referring to enum foo before you define it, and your friendly C compiler is using some sort of implicit int. Such an implicit declaration is no longer standard C and your code is not strictly portable.

Reference: http://en.cppreference.com/w/c/language/enum

like image 44
Bathsheba Avatar answered Sep 30 '22 14:09

Bathsheba