enum safety

S

Sard

Hi,

Page 39 K&R2 says

'Although variables of enum types may be declared, compilers need not
check that what you store in such a variable is a valid value for the
enumeration'

gcc produces an error for the code below as I'm trying to assign a
pointer to char to the a variable of type colour. Is gcc going beyond
the call of duty? Why would the standard allow compilers to accept
such code?

#include <stdio.h>
#include <limits.h>

char* c=0;

enum colours {
Red,Blue

};


int main ()
{
enum colours purple =c ;

return 0;
}
 
F

fred.l.kleinschmidt

Hi,

Page 39 K&R2 says

'Although variables of enum types may be declared, compilers need not
check that what you store in such a variable is a valid value for the
enumeration'

gcc produces an error for the code below as I'm trying to assign a
pointer to char to the a variable of type colour.  Is gcc going beyond
the call of duty?  Why would the standard allow compilers to accept
such code?

#include <stdio.h>
#include <limits.h>

char* c=0;

enum colours {
Red,Blue

};

int main ()
{
 enum colours purple =c ;

  return 0;



}- Hide quoted text -

- Show quoted text -

c is a pointer, not an integer.
It is not assignable to purple without a cast.
 
I

Ian Collins

Sard said:
Hi,

Page 39 K&R2 says

'Although variables of enum types may be declared, compilers need not
check that what you store in such a variable is a valid value for the
enumeration'
This is why (in my opinion) enums are horribly broken in C.
int main ()
{
enum colours purple =c ;
c is a pointer, enums are integer types.

You would get the same diagnostic with

int n = c;
 
K

Keith Thompson

Sard said:
Page 39 K&R2 says

'Although variables of enum types may be declared, compilers need not
check that what you store in such a variable is a valid value for the
enumeration'

Right. In fact, the above statement is too weak; compilers aren't
even allowed to perform such checks (or at least they're not allowed
to reject the program if such a check fails).
gcc produces an error for the code below as I'm trying to assign a
pointer to char to the a variable of type colour. Is gcc going beyond
the call of duty? Why would the standard allow compilers to accept
such code?

#include <stdio.h>
#include <limits.h>

char* c=0;

enum colours {
Red,Blue

};


int main ()
{
enum colours purple =c ;

return 0;
}

The compiler needn't check for a valid value. It must check for a
valid type. In this case, c is of type char*, purple is of type enum
colours, and there's no implicit conversion. This really has nothing
to do with it being an enum type.

What K&R is talking about is something like this:

#include <stdio.h>
int main(void)
{
enum colors { red = 10, blue = 20 };
enum colors purple = 15;
printf("purple = %d\n", (int)purple);
return 0;
}

This is perfectly legal; a particularly picky compiler might warn
about it, but it must accept it and it must produce the output
purple = 15

An enum type declaration really does two things. It creates an
enumerated type (which must be compatible with some existing integer
type and must be able to hold all the specified values), and it
creates a series of constants (which, oddly enough, are of type int,
not of the enumerated type). It looks a lot like the enumerated types
you might see in other languages such as Pascal, and you can use it
for the same purposes if you're careful, but it's really quite
different.

Note that this provides a cute mechanism for creating a named constant
of type int:

enum { MAX=1000 };

The type is anonymous and is never used; MAX is a constant of type int
with the value 1000. This is arguably cleaner than a macro, but it
doesn't extend to types other than int.
 
E

Eric Sosman

Keith said:
[...]
What K&R is talking about is something like this:

#include <stdio.h>
int main(void)
{
enum colors { red = 10, blue = 20 };
enum colors purple = 15;
printf("purple = %d\n", (int)purple);
return 0;
}

This is perfectly legal; a particularly picky compiler might warn
about it, but it must accept it and it must produce the output
purple = 15

Another (and perhaps more plausible) use of non-enumerated
enum values is in constructing sets of flags by OR-ing named
values together:

enum {
PRESENT = 0x1, CORRECT = 0x2, SALUTING = 0x4
} status;
status = PRESENT | CORRECT;
puts ("All present and correct, sir!");
receive_stern_look(from_officer);
status |= SALUTING;
puts ("Aye, aye, sir!");
 
A

Army1987

Ian said:
This is why (in my opinion) enums are horribly broken in C.
Not necessarily. For example, you can have an object which can contain
a number up to 10 as numbers, and 11, 12 and 13 with special meanings. E.g.
enum rank { ACE = 1, JACK = 11, QUEEN = 12, KING = 13 };
enum rank foo = 7;
(For example mbrtowc can return a number of bytes, or 0, (size_t)(-2) and
(size_t)(-1) with special meanings. Here an enum won't do it because
constants must fit in an int, but one might have an int-returning function
with similar semantics. Maybe using negative numbers:
enum err_code { OUT_OF_MEMORY = -2, IO_ERROR = -1, MAX = INT_MAX };
 
I

Ian Collins

Army1987 said:
Not necessarily. For example, you can have an object which can contain
a number up to 10 as numbers, and 11, 12 and 13 with special meanings. E.g.
enum rank { ACE = 1, JACK = 11, QUEEN = 12, KING = 13 };
enum rank foo = 7;

That illustrates the problem perfectly, you declare declare a valid
range of values for rank and then legally assign some other arbitrary
value to it. Useless.
 
A

Anonymous

That illustrates the problem perfectly, you declare declare a valid
range of values for rank and then legally assign some other arbitrary
value to it. Useless.

enum SomeFlags {
FLAG_NONE = 0,
FLAG_FROB = 1 << 0,
FLAG_BARF = 1 << 1,
FLAG_FISH = 1 << 2,
};

enum SomeFlags = FLAG_FROB | FLAG_FISH;

Aribitrary value assigned. Still useful.
 
K

Keith Thompson

Ian Collins said:
That illustrates the problem perfectly, you declare declare a valid
range of values for rank and then legally assign some other arbitrary
value to it. Useless.

It seems perfectly useful to me. An object of type enum range can
sensibly hold any value from 1 to 13. The values 1, 11, 12, and 13
happen to have names associated with them; the others are merely
numbers. The language guarantees that this will work (in fact, it
guarantees that values in the range 0..127 are valid).

A C enumerated type doesn't act like an enumerated type in, say,
Pascal or Ada, but it's not supposed to. It merely provides a set of
names for specific values, and an integral type that can hold all
those values.
 
S

Serve Laurijssen

Anonymous said:
enum SomeFlags {
FLAG_NONE = 0,
FLAG_FROB = 1 << 0,
FLAG_BARF = 1 << 1,
FLAG_FISH = 1 << 2,
};

enum SomeFlags = FLAG_FROB | FLAG_FISH;

Aribitrary value assigned. Still useful.

Not really, using an enum here is useless. In fact, in some coding standard
(misra I think) its forbidden to use enums in this way. I dont want to argue
about that ruke, but it does show there are other ways in C to accomplish
this and not by abusing enum
 
E

Eric Sosman

Missing an identifier here ...
Not really, using an enum here is useless. In fact, in some coding
standard (misra I think) its forbidden to use enums in this way. I dont
want to argue about that ruke, but it does show there are other ways in
C to accomplish this and not by abusing enum

(Shrug.) The alternative would be something like

typedef unsigned int Flagset;
#define FLAG_NONE 0
#define FLAG_FROB (1 << 0)
#define FLAG_BARF (1 << 1)
#define FLAG_FISH (1 << 2)
Flagset kirsten = FLAG_FROB | FLAG_FISH;

.... which seems a pretty close equivalent. In particular, it
is neither more nor less risky than using an enum.
 
I

Ian Collins

Keith said:
It seems perfectly useful to me. An object of type enum range can
sensibly hold any value from 1 to 13. The values 1, 11, 12, and 13
happen to have names associated with them; the others are merely
numbers. The language guarantees that this will work (in fact, it
guarantees that values in the range 0..127 are valid).

A C enumerated type doesn't act like an enumerated type in, say,
Pascal or Ada, but it's not supposed to. It merely provides a set of
names for specific values, and an integral type that can hold all
those values.
Or in C++, where they are first class types. A C enum is little
different from a typedef and a set of #defines. It's the lack of type
safety I don't like. Maybe it as was a bad experience with come code
that tried to use enems as types, but ended up abusing them (assigning
wrong values) that made me feel this way.
 
K

Keith Thompson

Ian Collins said:
Keith Thompson wrote: [...]
A C enumerated type doesn't act like an enumerated type in, say,
Pascal or Ada, but it's not supposed to. It merely provides a set of
names for specific values, and an integral type that can hold all
those values.
Or in C++, where they are first class types. A C enum is little
different from a typedef and a set of #defines.

Exactly (except that the names of the constants follow the scoping
rules of ordinary identifiers rather than of macros, which IMHO is a
considerable advantage).
It's the lack of type
safety I don't like.

Welcome to C! :cool:}
Maybe it as was a bad experience with come code
that tried to use enems as types, but ended up abusing them (assigning
wrong values) that made me feel this way.

A C enumerated type is what it is. No more, no less. As long as you
keep that in mind, and *don't* confuse it with similar features in
other languages, they're not that hard to use.

You can have an enumerated type for which only the named values are
considered valid. Assigning some other value is then a logical bug in
your program, one that the compiler unfortunately won't help you
detect (unless you write your own range-checking code). But nothing
else in C will detect this kind of logical error either (unless you
write your own range-checking code). What the "enum" construct gives
you is a convenient way for the compiler to assign consecutive values
to your constants, and to choose a type that can hold all of them.

Or you can have an enumerated type where only some of the valid values
have names assigned to them, and assigning some other value is not an
error:
enum card_rank { ace = 1, jack = 11, queen = 12, king = 13 };

Or you can have an anonymous enumerated type if all you're interested
in is the constants:
enum { MAX_LENGTH = 256 };

It might have made more sense to have three different language
constructs for these three things. But if we were going to insist on
everything making sense, we probably wouldn't be using C.
 

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

Similar Threads


Members online

No members online now.

Forum statistics

Threads
473,756
Messages
2,569,534
Members
45,007
Latest member
OrderFitnessKetoCapsules

Latest Threads

Top