Enums without identifier, enums and typedef

G

Guest

I came over this code which puzzled me. Isn't the enum supposed to
have an identifier?

enum {
BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */
BT_OPEN,
BT_BOUND,
BT_LISTEN,
BT_CONNECT,
BT_CONNECT2,
BT_CONFIG,
BT_DISCONN,
BT_CLOSED
};


Following this, I played around a bit, and came up with the program
below, which I had hoped would provoke the compiler into issuing a
warning about type mismatch (or something along those lines). Which
it didn't. (gcc -std=c99 -Wall -pedantic).

Care to comment?

With kind regards
Asbjørn Sæbø


#include <stdio.h>

typedef enum
{
M_A = 1,
M_B,
M_C,
M_D
} M_type;


typedef enum
{
N_A = 1,
N_B,
N_C,
N_D
} N_type;


void myfunc(M_type myvar)
{
printf("Inside myfunc, myvar is %d\n", myvar);
}


int main(void)
{
N_type a = N_B;

myfunc(a); /* Feed variable of N_type into function expecting M_type */
return 0;
}
 
R

Richard Bos

=?utf-8?b?QXNiasO4cm4gU8OmYsO4?= said:
I came over this code which puzzled me. Isn't the enum supposed to
have an identifier?

Not necessarily.
enum {
BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */
BT_OPEN,
BT_BOUND,
BT_LISTEN,
BT_CONNECT,
BT_CONNECT2,
BT_CONFIG,
BT_DISCONN,
BT_CLOSED
};

This doesn't define a type, but it does define a number of constants.
This can on occasion be quite useful. It is, for example, easier to
maintain than a series of #defines.

Richard
 
M

mark_bluemel

Asbjørn Sæbø said:
I came over this code which puzzled me. Isn't the enum supposed to
have an identifier?

enum {
BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */
BT_OPEN,
BT_BOUND,
BT_LISTEN,
BT_CONNECT,
BT_CONNECT2,
BT_CONFIG,
BT_DISCONN,
BT_CLOSED
};

Does the FAQ entry <http://c-faq.com/struct/enumvsdefine.html> help?

Basically, I think it boils down to this - enums are for historical
reasons, rather weakly specified. So in effect, you are just creating
some integral constants here. Likewise in your example program, because
of the weak specification values from one enum can be used where
another is specified - enum types are "just" some integer type.
 
D

David T. Ashley

Asbjørn Sæbø said:
I came over this code which puzzled me. Isn't the enum supposed to
have an identifier?

enum {
BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */
BT_OPEN,
BT_BOUND,
BT_LISTEN,
BT_CONNECT,
BT_CONNECT2,
BT_CONFIG,
BT_DISCONN,
BT_CLOSED
};

Looks fine to me.

I don't know the standards -- I just have years of messing around with
compilers.

Based on years of messing around and randomly modifying code until it seems
to work most of the time ... an enum is generally interchangeable with an
integer, and the compiler won't enforce type. Passing an integer where an
enum is "expected" or passing an enumerated constant where an integer is
expected seems to generate no warnings or errors.

I don't know the standards, but the code excerpt above behaves about the
same as:

#define BT_CONNECTED (1)
#define BT_OPEN (2)
etc.

The only advantage to enums seems to be that they allow the compiler to
number the constants itself. In fact, in the example you gave, my code
would also traditionally include a symbol like "BT_MAX" at the end to test
for a corrupted or unexpected variable (and of course, the value of BT_MAX
will be modified if constants are added or removed earlier in the list).

Enums in C are treated oddly--the typing is very weak. The automatic
numbering of the constants is the only aspect of it I've found useful.
 
K

Keith Thompson

Asbjørn Sæbø said:
I came over this code which puzzled me. Isn't the enum supposed to
have an identifier?

enum {
BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */
BT_OPEN,
BT_BOUND,
BT_LISTEN,
BT_CONNECT,
BT_CONNECT2,
BT_CONFIG,
BT_DISCONN,
BT_CLOSED
};

Perfectly legal. The tag on an enum type declaration is optional.
The declaration creates a type (an anonymous one in this case),
but the constants BT_CONNECTED et al are actually of type int.

For the above declaration, the creation of the type (which can't be
used anyway) is a side effect; the purpose of the declaration is to
declare the constants. It's a cleaner alternatve to

#define BT_CONNECTED 1
#define BT_OPEN 2
...
Following this, I played around a bit, and came up with the program
below, which I had hoped would provoke the compiler into issuing a
warning about type mismatch (or something along those lines). Which
it didn't. (gcc -std=c99 -Wall -pedantic).
[...]


#include <stdio.h>

typedef enum
{
M_A = 1,
M_B,
M_C,
M_D
} M_type;


typedef enum
{
N_A = 1,
N_B,
N_C,
N_D
} N_type;


void myfunc(M_type myvar)
{
printf("Inside myfunc, myvar is %d\n", myvar);
}


int main(void)
{
N_type a = N_B;

myfunc(a); /* Feed variable of N_type into function expecting M_type */
return 0;
}

There's no problem with the code. Values of enum or integer types may
be implicitly converted back and forth.

It would have been nice, in some ways, if the rules for enumerated
types were more strict (as they are in some other languages), but
they're not.
 
B

Ben Pfaff

Asbjørn Sæbø said:
enum {
BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */

Putting this in a comment is perverse. I would write this as:
BT_CONNECTED = TCP_ESTABLISHED,
and then there's no need for the comment at all.
 
R

Richard Tobin

enum {
BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */

Putting this in a comment is perverse. I would write this as:
BT_CONNECTED = TCP_ESTABLISHED,
and then there's no need for the comment at all.[/QUOTE]

Assuming that TCP_ESTABLISHED is defined at that point, which it might
well not be.

-- Richard
 
Y

Yevgen Muntyan

Asbjørn Sæbø said:

Yes. Although this probably means that the practice I have seen of
typedef-ings enums does not help much when it comes to catching
errors.

gcc (and all others out there too, probably) checks switch statements
for you:

typedef enum {
ONE,
TWO
} Something;

void func (Something s)
{
switch (s)
{
case ONE:
break;
} /* warning here - TWO not handled in switch */
}

And using enum type instead of int is a good documentation too,
which is a great help to *avoid* errors in the first place.

Regards,
Yevgen
 
K

Keith Thompson

Asbjørn Sæbø said:

Yes. Although this probably means that the practice I have seen of
typedef-ings enums does not help much when it comes to catching
errors.

As far as I can tell, typedef-ing an enum doesn't help *at all* when
it comes to catching errors. A typedef merely creates an alias for a
type; it doesn't create a new type.

Typedefs can be useful when creating an abstract data type (hiding the
representation details from clients), or when specifying a numeric
type that may vary from one implementation to another (such as
int32_t, which could be int, long, etc.) Most other uses of typedefs
are, in my opinion, not useful; you might as well refer to the
original type directly.
 
K

Keith Thompson

David T. Ashley said:
Asbjørn Sæbø said:
I came over this code which puzzled me. Isn't the enum supposed to
have an identifier?

enum {
BT_CONNECTED = 1, /* Equal to TCP_ESTABLISHED to make net code happy */
BT_OPEN,
BT_BOUND,
BT_LISTEN,
BT_CONNECT,
BT_CONNECT2,
BT_CONFIG,
BT_DISCONN,
BT_CLOSED
};
[snip]
I don't know the standards, but the code excerpt above behaves about the
same as:

#define BT_CONNECTED (1)
#define BT_OPEN (2)
etc.

Yes, pretty much. Incidentally, the parentheses aren't necessary.
Parenthesizing macro definitions is an excellent habit, but it's good
to know that if the definition is a single literal constant, there's
no way for operator precedence to cause problems.

Of course the parentheses are harmless; this is an idle comment, not a
complaint.
The only advantage to enums seems to be that they allow the compiler to
number the constants itself.
Yup.

In fact, in the example you gave, my code
would also traditionally include a symbol like "BT_MAX" at the end to test
for a corrupted or unexpected variable (and of course, the value of BT_MAX
will be modified if constants are added or removed earlier in the list).

Which you can do like this:

enum {
BT_MIN = 1,
BT_CONNECTED = BT_MIN,
BT_OPEN,
BT_BOUND,
BT_LISTEN,
BT_CONNECT,
BT_CONNECT2,
BT_CONFIG,
BT_DISCONN,
BT_CLOSED,
BT_MAX = BT_CLOSED
};

You still have to fix things if you change the first or last literal,
but not if you add or remove something in the middle.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,733
Messages
2,569,440
Members
44,832
Latest member
GlennSmall

Latest Threads

Top