0

How can C compile and run the following code without errors ?

#include <stdio.h>

enum
{
    DETAILS_A                       =0x00U,
    DETAILS_B                       =0x01U,
    DETAILS_C                       =0x02U
}u8_Details;

int main()
{
    
    u8_Details = 42;
    
    printf("%d\n\n", u8_Details);
    
    printf("%d", DETAILS_B);

    return 0;
}

Console ouput:

42

1

...Program finished with exit code 0
Press ENTER to exit console

If I understand correctly what's happening here, it's like this line:

u8_Details = 42;

was equivalent to this :

u8_Details u8_Details = 42;

But that just seems so wrong. What am I missing ?

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
erenaud
  • 23
  • 7
  • 1
    An enum isn't limited to the values in the enum. That's just the way it is. Validation, if desired, is left to the user. – ikegami Dec 13 '21 at 14:52
  • TLDR: because that's what the C standard says – Andrew Henle Dec 13 '21 at 14:53
  • 4
    Did you mean to write `typedef enum { ... } u8_Details`? As the code stands, you've created an enum (with no name), and a variable `u8_Details` whose type is that enum. – psmears Dec 13 '21 at 14:56
  • This might be of interest: [How to create type safe enums?](https://stackoverflow.com/questions/43043246/how-to-create-type-safe-enums) – Lundin Dec 13 '21 at 14:56
  • @ikegami Thanks but I think you are missing my point. I am not bothered by the fact that I am affecting 42 to a variable, I am bothered by the fact that I didn't even declared any variable. And I am basically affecting 42 to a type – erenaud Dec 13 '21 at 14:58
  • @psmears "Did you mean to write typedef enum { ... } u8_Details?" I know this would be the solution, but I was expecting current code to raise an error. "As the code stands, you've created an enum (with no name), and a variable u8_Details whose type is that enum." Ok, that's exactly the explanation I was looking for. Thanks ! – erenaud Dec 13 '21 at 15:00
  • 1
    Re "*I am bothered by the fact that I didn't even declared any variable.*", uh, what do you think `TYPE u8_Details;` does? – ikegami Dec 13 '21 at 15:23
  • @ikegami As others pointed out, not being aware of that was indeed my mistake, yes. – erenaud Dec 13 '21 at 15:39
  • It was a rhetorical question. You use `TYPE var;` to declare vars all over the place. – ikegami Dec 13 '21 at 15:43
  • Another side note could be your name u8_Details sort of implies that the variable is unsigned 8bits, but generally with enum you don't really know the underlying type. There are compiler switches that let you set the size but generally I believe they are just whatever int maps to. – bd2357 Dec 13 '21 at 22:43

3 Answers3

3

In this declaration

enum
{
    DETAILS_A                       =0x00U,
    DETAILS_B                       =0x01U,
    DETAILS_C                       =0x02U
}u8_Details;

you declared the variable u8_Details of an unnamed enumeration type in the file scope.

You may assign a value to the variable as to any other mutable variable.

As for your assumption

u8_Details u8_Details = 42;

then it is wrong. u8_Details is not a type specifier. It is an identifier of a variable of an unnamed enumeration type.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
2

u8_Details is not the enum (type) but a variable of that enum (type).

enum {...} foo;

is a variable definition statement. The enum {...} is the type, and foo is the identifier.

if you want make foo a type, use typedef:

typedef enum {...} foo; /* foo is an alias of type enum {...} now */
foo bar; /* bar is of type foo */
Todd Wong
  • 234
  • 1
  • 4
2

Vlad answered the question correctly, but I want to add something:

C has four different name spaces for identifiers:

  • label names (disambiguated by goto and :);
  • tag names (disambiguated by struct, union, or enum);
  • struct and union member names (disambiguated by . or -> operators and presence in a definition);
  • all other identifiers (variable names, function names, typedef names, enumeration constants, etc.);

The same identifier can be used in different names spaces in the same scope - IOW, the following is perfectly legal (just really bad practice):

int main( void )
{
  struct foo {   // tag namespace
  {
    int foo;     // member namespace
    int bar;
  } foo;         // all other identifiers namespace
  
  goto foo;      // label namespace
  ...
  foo:           // label namespace
  foo.foo = 1;   // all other identifiers and member namespaces
  ...
}

The identifier foo can be used as a struct tag name, a struct member name, a label, and a variable name, all in the same scope.

Each struct or union definition creates a new member namespace, so you can use foo as a member name in different struct or union definitions:

 struct a { int foo; ... };
 struct b { double foo; ... };

Enumeration constants and typedef names belong to the "all other identifiers" namespace, so if you use foo as an enumeration constant, you cannot also use it as a regular variable name or function name or typedef name in the same scope (and vice versa).

There is only one tag namespace, so you can't use the same tag name for a struct type and a union type in the same scope - IOW, if you have struct foo, you cannot also have union foo and enum foo in the same scope.

John Bode
  • 119,563
  • 19
  • 122
  • 198