enum as bitfields

Q

Quentin Pope

Hello,

Does the C standard say anything about using enum in a bit field? Like

enum {
apple,
orange

} fruit;

struct {
enum fruit f:16;

};

Gcc does not complain about this when -ansi or -pedantic is turned
OFF. XLC (claims to be an ANSI C compiler) on the IBM RS/6000 issued
a warning and treat it like an unsigned integer. K&R 2nd Ed says
nothing explicitly about this but it says that bit fields are
`implementation dependant' and `always unsigned'.

I think this is very useful and in our project we encourage people to use
enum instead of #define to create constants. Although not used in a lot
of places, the bit field somewhat defeats this purpose. Is there a
rationale why this is so? Is there any way to shut up the compiler?
I've checked the FAQ. No answers from there.

Thanks
QP
 
J

James Kuyper

Hello,

Does the C standard say anything about using enum in a bit field? Like

enum {
apple,
orange

} fruit;

struct {
enum fruit f:16;

};

Gcc does not complain about this when -ansi or -pedantic is turned
OFF. XLC (claims to be an ANSI C compiler) on the IBM RS/6000 issued
a warning and treat it like an unsigned integer.

6.7.2.1p4: "A bit-field shall have a type that is a qualified or
unqualified version of _Bool, signed int, unsigned int, or some other
implementation-defined type."

This means that an implementation can accept other types, in which case
no diagnostic is required, but that an implementation is not required to
accept any other types. Code that's intended to be portable should
therefore not use other types.
... K&R 2nd Ed says
nothing explicitly about this but it says that bit fields are
`implementation dependant' and `always unsigned'.

Many features of bit fields are implementation-dependent; but it's not
clear which of those features that comment might be about. A bit field
declared as 'signed int' is unambiguously signed. Are you sure that K&R2
claims that bit-fields are "always unsigned"?

6.7.2.1p4 seems to imply that an implementation is not required to
accept a bit field declared with plain 'int', something I had not
noticed before. However, if an implementation does accept it, then
6.7.2p5 applies: "for bit-fields, it is implementation-defined whether
the specifier int designates the same type as signed int or the same
type as unsigned int." From this I conclude that, even if an
implementation does allow use of plain 'int' for bit fields, no sane
programmer should ever take advantage of that fact.
 
K

Keith Thompson

Quentin Pope said:
Does the C standard say anything about using enum in a bit field? Like

enum {
apple,
orange

} fruit;

struct {
enum fruit f:16;

};

Yes. C99 6.7.2.1p4:

A bit-field shall have a type that is a qualified or unqualified
version of _Bool, signed int, unsigned int, or some other
implementation-defined type.

This is a constraint, and "implementation-defined" means that the
ipmlementation must document any other types, so the compiler (in
conforming mode) must complain about an enum bit-field *unless*
the compiler's documentation specifically says that it's permitted.

More generally, using a bit field of a type other than the ones
listed in the standard makes your code non-portable. (Using bit
fields is likely to do that as well, if you're assuming that it
defines the layout precisely.)

[...]
I think this is very useful and in our project we encourage people to use
enum instead of #define to create constants. Although not used in a lot
of places, the bit field somewhat defeats this purpose. Is there a
rationale why this is so? Is there any way to shut up the compiler?
I've checked the FAQ. No answers from there.

In the example you've shown us, making "f" a bit field doesn't make much
sense; it would be simpler to write just "enum fruit f;". Why do you
need f to be exactly 16 bits?

If your purpose is to define some constants, you can use enumerations
without necessarily using the enumeration type. For example:

enum { apple, orange };
struct basket { unsigned f:16; };
struct basket b;

b.f = apple;

Enumeration constants in C are not actually of the enumeration type;
they're always of type int.
 
B

Ben Bacarisse

Quentin Pope said:
Does the C standard say anything about using enum in a bit field? Like

enum {
apple,
orange

} fruit;

(you mean enum fruit {...}; not enum {...} fruit; I think)
struct {
enum fruit f:16;

};

Yes, it does. There is a constraint:

A bit-field shall have a type that is a qualified or unqualified
version of _Bool, signed int, unsigned int, or some other
implementation-defined type.

which, if violated would make your program undefined and would require a
diagnostic. However, from the wording it would seem that an
implementation that documents the possibility of using an enum, may
accept this code without saying a word.
Gcc does not complain about this when -ansi or -pedantic is turned
OFF. XLC (claims to be an ANSI C compiler) on the IBM RS/6000 issued
a warning and treat it like an unsigned integer. K&R 2nd Ed says
nothing explicitly about this but it says that bit fields are
`implementation dependant' and `always unsigned'.

You may be misreporting this. K&R2 sticks close to what was called ANSI
C, and in ANSI C, bit fields can be signed. My quote above is from the
newer 1999 C standard.
I think this is very useful and in our project we encourage people to use
enum instead of #define to create constants. Although not used in a lot
of places, the bit field somewhat defeats this purpose. Is there a
rationale why this is so? Is there any way to shut up the compiler?

There might be. You name two compilers and the one that complains is
one I don't know. gcc also has something to say if put into conforming
mode, though what it does is simply to remind you that this is an
extension:

$ gcc -std=c99 -pedantic -c x.c
x.c:8:8: warning: type of bit-field ‘f’ is a GCC extension

I don't think this is one you can turn off. My reading of the standard
is that gcc could silently accept this code in conforming mode, but it
is not obliged to do so. A compiler is permitted to issue pretty much
any diagnostics it likes, as far as the standard is concerned.
 
B

Ben Pfaff

Quentin Pope said:
Does the C standard say anything about using enum in a bit field?

It doesn't say that it's allowed, except as an implementation
extension:

4 A bit-field shall have a type that is a qualified or
unqualified version of _Bool, signed int, unsigned int, or
some other implementation-defined type.
 
K

Keith Thompson

James Kuyper said:
6.7.2.1p4: "A bit-field shall have a type that is a qualified or
unqualified version of _Bool, signed int, unsigned int, or some other
implementation-defined type." [...]

6.7.2.1p4 seems to imply that an implementation is not required to
accept a bit field declared with plain 'int', something I had not
noticed before.

I don't think so. The same reasoning would imply that you can't declare
a bit-field as "unsigned", "int unsigned", or "signed". 6.7.2p2
indicates that "int", "signed", and "signed int" are all names for the
same type.

I'm 99% sure that the intent is implementations are required to support
that plain "int" bit-fields.
 
H

Harald van Dijk

[...]> 6.7.2.1p4: "A bit-field shall have a type that is a qualified or
unqualified version of _Bool, signed int, unsigned int, or some other
implementation-defined type."
[...]

6.7.2.1p4 seems to imply that an implementation is not required to
accept a bit field declared with plain 'int', something I had not
noticed before.

I don't think so.  The same reasoning would imply that you can't declare
a bit-field as "unsigned", "int unsigned", or "signed".  6.7.2p2
indicates that "int", "signed", and "signed int" are all names for the
same type.

Bit-fields are a special case where int and signed int are not
necessarily synonymous: plain int may be treated as either signed or
unsigned. However, whichever it is, it is not like char, where plain
char is a distinct type from the signed and unsigned varieties.
6.7.2p5 makes it clear that plain int is either signed int, or
unsigned int.
I'm 99% sure that the intent is implementations are required to support
that plain "int" bit-fields.

Sure.
 
Q

Quentin Pope

(you mean enum fruit {...}; not enum {...} fruit; I think)


Yes, it does. There is a constraint:

A bit-field shall have a type that is a qualified or unqualified
version of _Bool, signed int, unsigned int, or some other
implementation-defined type.

which, if violated would make your program undefined and would require a
diagnostic. However, from the wording it would seem that an
implementation that documents the possibility of using an enum, may
accept this code without saying a word.

So in effect, it may or may not be a constraint violation, depending on
whether the underlying type of the enum is int, unsigned int or something
else?
 
J

James Kuyper

So in effect, it may or may not be a constraint violation, depending on
whether the underlying type of the enum is int, unsigned int or something
else?

No - the underlying type of the enum is irrelevant. What is relevant is
the implementation's documentation: any additional types that the
documentation says are acceptable may be used. Since "A bit-field is
interpreted as a signed or unsigned integer type" (6.7.2.1p9), it
wouldn't make much sense for an implementation to accept non-integral
types. However, as far as the constraint listed above is concerned, if
an implementation were to document that it accepts 'double' as a type
for a bit-field, then even "double f:16" would not be a violation of the
constraint listed above.

Of course, portable code should only use the types that are guaranteed
to be allowed.
 
Q

Quentin Pope

No - the underlying type of the enum is irrelevant. What is relevant is
the implementation's documentation: any additional types that the
documentation says are acceptable may be used. Since "A bit-field is
interpreted as a signed or unsigned integer type" (6.7.2.1p9), it
wouldn't make much sense for an implementation to accept non-integral
types. However, as far as the constraint listed above is concerned, if
an implementation were to document that it accepts 'double' as a type
for a bit-field, then even "double f:16" would not be a violation of the
constraint listed above.

Of course, portable code should only use the types that are guaranteed
to be allowed.

James

I think you may have misunderstood what I was saying.

My point was that if the enum type is an alias for int, then that is one
of the allowed types for a bitfield with no need for any implementation
documentation.

So although the code won't be portable to every implementation, it will
still be portable (no constraint violation) for all implementations where
enums have type int.
 
H

Harald van Dijk

No - the underlying type of the enum is irrelevant. [...]
Of course, portable code should only use the types that are guaranteed
to be allowed.

James

I think you may have misunderstood what I was saying.

My point was that if the enum type is an alias for int, then that is one
of the allowed types for a bitfield with no need for any implementation
documentation.

An enum type is never an alias for int. If the underlying type is int,
then the enum type is compatible with int, but it is still a different
type, in much the same way that int[] and int[3] are compatible but
different types. Does this affect programs aside from bit-fields? I
believe so:

#include <limits.h>
enum INT1 { MIN1 = INT_MIN, MAX1 = INT_MAX }; /* Assume INT1 is
compatible with int */
enum INT2 { MIN2 = INT_MIN, MAX2 = INT_MAX }; /* Assume INT2 is
compatible with int too */

int a;
enum INT1 a; /* Valid. int and enum INT1 are compatible */

int b;
enum INT2 b; /* Also valid. int and enum INT2 are compatible */

enum INT1 c;
enum INT2 c; /* Constraint violation. enum INT1 and enum INT2 are not
compatible. */

Actually, this is something that the usually strictest compiler I have
gets wrong, which accepts even the last declaration. GCC and ICC get
it right and reject it.
 
J

James Kuyper

I think you may have misunderstood what I was saying.
Apparently.

My point was that if the enum type is an alias for int, then that is one

An enum type is not an alias for an int. In C, types are aliased only
through the typedef mechanism. Every enumerated type is required to be
"compatible with char, a signed integer type, or an
unsigned integer type" (6.7.2.2p4), but compatible types are not aliases
for each other. The standard often allow one type to be used in place of
a compatible type, but that's only true when the standard explicitly
says so; this is not one of those cases.
of the allowed types for a bitfield with no need for any implementation
documentation.

I didn't realize that this was the point you were making, in part
because your point is incorrect. An enumerated type that's compatible
with 'int' does not qualify as actually being an 'int' for this purpose
(except insofar as a particular implementation allows it).
So although the code won't be portable to every implementation, it will
still be portable (no constraint violation) for all implementations where
enums have type int.

A conforming implementation could make your enumeration compatible with
signed int, and still refuse to compile such a program by reason of
violating that constraint.
 

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,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top