Bitfield casting

A

arne

Hi all,

cleaning up some elderly code, I stumbled across the following:

/**************************************************/

struct {
uint bf:8;
char a1[8];
char a2[8];
} binfo;

/* prototype added later */
int foo( char * );

int main( void )
{
foo( &binfo );

return 0;
}

int foo( char *c ) {

/*
** do something with c here ...
*/

return 0;
}

/**************************************************/

Note: The address of the bitfield bf of the struct binfo is used as
the
char* arg in foo().

The compiler was complaining, since there was no prototype for foo().
After adding it, the compiler was complaining, since the call of foo()
did
not match the prototype (which I can understand, since &binfo is
certainly
not exactly of type char*).

None the less, I have some questions:

1. Does it change the semantic of the code in any circumstances
(little
vs big endian, 64 bit architectures, ...) if I cast the argument of
foo like

foo( (char *)&binfo );

?
If the explicit cast is omitted, isn't the same cast done implicitely
anyway? If
yes: also if there is no prototype or what happens then?

2. Is &binfo _always_ pointing to the 8 bit wide bitfield bf (more
general:
are the addresses of a struct and of its first member always
identical)?

3. Are the addresses of members in a struct always ascending in the
same
order as the members have been defined? Or is the compiler allowed to
do some reordering (not padding)?


Thanks in advance for your responses!
Arne
 
E

Eric Sosman

arne wrote On 03/06/07 15:07,:
Hi all,

cleaning up some elderly code, I stumbled across the following:

/**************************************************/

struct {
uint bf:8;

I'll assume `uint' is a typedef alias for `unsigned int'.
char a1[8];
char a2[8];
} binfo;

/* prototype added later */
int foo( char * );

int main( void )
{
foo( &binfo );

return 0;
}

int foo( char *c ) {

/*
** do something with c here ...
*/

return 0;
}

/**************************************************/

Note: The address of the bitfield bf of the struct binfo is used as
the
char* arg in foo().

The compiler was complaining, since there was no prototype for foo().
After adding it, the compiler was complaining, since the call of foo()
did
not match the prototype (which I can understand, since &binfo is
certainly
not exactly of type char*).

None the less, I have some questions:

1. Does it change the semantic of the code in any circumstances
(little
vs big endian, 64 bit architectures, ...) if I cast the argument of
foo like

foo( (char *)&binfo );

The cast will (probably) cause the compiler to stop
complaining, but won't fix the latent bug.
?
If the explicit cast is omitted, isn't the same cast done implicitely
anyway?

No; that's why the compiler complains.
If yes: also if there is no prototype or what happens then?

If there is no prototype, then the argument &binfo is
simply *assumed* to be of the type foo() expects to receive.
Since it isn't of the right type, you get undefined behavior.
2. Is &binfo _always_ pointing to the 8 bit wide bitfield bf

&binfo is *never* pointing to the bit-field bf, because
bitfields can't be pointed to at all.
(more
general:
are the addresses of a struct and of its first member always
identical)?

Yes, a pointer to a struct can always be converted to a
pointer to its first element, and vice versa. But the first
element of binfo is not the bit-field bf; instead, it is the
"addressable storage unit" that holds the bit-field. That
"unit" has at least eight bits, but may have more -- and if
it does, you don't know which bits are part of bf and which
are merely along for the ride.
3. Are the addresses of members in a struct always ascending in the
same
order as the members have been defined? Or is the compiler allowed to
do some reordering (not padding)?

Padding is permitted anywhere except before the first
element. Reordering is not permitted, so a2 comes after a1
which in turn comes after the "unit" that holds bf.
 
K

Keith Thompson

arne said:
cleaning up some elderly code, I stumbled across the following:

/**************************************************/

struct {
uint bf:8;
char a1[8];
char a2[8];
} binfo;

/* prototype added later */
int foo( char * );

int main( void )
{
foo( &binfo );

return 0;
}

int foo( char *c ) {

/*
** do something with c here ...
*/

return 0;
}

/**************************************************/

Note: The address of the bitfield bf of the struct binfo is used as
the char* arg in foo().
[snip]

No, it isn't; bit fields don't have addresses.

There is no predefined type called "uint". I presume it's a typedef
for "unsigned int"; it would be better just to use "unsigned int"
explicitly.

Once you "clean up" the code, probably by judiciously adding casts,
it's likely to do what you want, but there's no guarantee. The
standard guarantees that a bit field will occupy the specified number
of bits, but not much else (there are some additional guarantees, but
they leave enough wiggle room for the compiler to do some odd things).

If you want the first member of your structure to be 8 bits and
unsigned, *and* if you're able to assume CHAR_BIT==8, then you should
just declare it as an unsigned char, not as a bit field.

Any object can be treated as an array of bytes (unsigned char is
usually better than plain char for this), but code that does so is
likely to depend on the compiler-specific representation of the type.
 
J

Jack Klein

arne said:
cleaning up some elderly code, I stumbled across the following:

/**************************************************/

struct {
uint bf:8;
char a1[8];
char a2[8];
} binfo;

[snip]
If you want the first member of your structure to be 8 bits and
unsigned, *and* if you're able to assume CHAR_BIT==8, then you should
just declare it as an unsigned char, not as a bit field.

Why the need to assume CHAR_BIT 8? If you want the first member of
your structure to hold 8 bits and be unsigned, regardless of whether
you use an unsigned char and bit fiddle, or use a bit-field containing
8 bits, it will occupy at least the first byte of your structure,
regardless of whether CHAR_BIT is 8, 16, 32, or some other value.

In fact the one guarantee that you have is that if you define it as an
unsigned char, it will occupy exactly one byte at the beginning of
your structure, whereas a bit-field containing 8 bits might occupy
more than one byte, whatever the implementation's value of CHAR_BIT.

Of course, depending on what follows, padding after the first member
might moot the difference.
 
K

Keith Thompson

Jack Klein said:
arne said:
cleaning up some elderly code, I stumbled across the following:

/**************************************************/

struct {
uint bf:8;
char a1[8];
char a2[8];
} binfo;
[snip]

If you want the first member of your structure to be 8 bits and
unsigned, *and* if you're able to assume CHAR_BIT==8, then you should
just declare it as an unsigned char, not as a bit field.

Why the need to assume CHAR_BIT 8?

I was assuming the OP wanted *exactly* 8 bits; I should have said so.
If you want the first member of
your structure to hold 8 bits and be unsigned, regardless of whether
you use an unsigned char and bit fiddle, or use a bit-field containing
8 bits, it will occupy at least the first byte of your structure,
regardless of whether CHAR_BIT is 8, 16, 32, or some other value.

In fact the one guarantee that you have is that if you define it as an
unsigned char, it will occupy exactly one byte at the beginning of
your structure, whereas a bit-field containing 8 bits might occupy
more than one byte, whatever the implementation's value of CHAR_BIT.

An 8-bit bit field will occupy exactly 8 bits; it can be less than a
byte, but it can't be more.
 
J

Jack Klein

Jack Klein said:
cleaning up some elderly code, I stumbled across the following:

/**************************************************/

struct {
uint bf:8;
char a1[8];
char a2[8];
} binfo;
[snip]

If you want the first member of your structure to be 8 bits and
unsigned, *and* if you're able to assume CHAR_BIT==8, then you should
just declare it as an unsigned char, not as a bit field.

Why the need to assume CHAR_BIT 8?

I was assuming the OP wanted *exactly* 8 bits; I should have said so.
If you want the first member of
your structure to hold 8 bits and be unsigned, regardless of whether
you use an unsigned char and bit fiddle, or use a bit-field containing
8 bits, it will occupy at least the first byte of your structure,
regardless of whether CHAR_BIT is 8, 16, 32, or some other value.

In fact the one guarantee that you have is that if you define it as an
unsigned char, it will occupy exactly one byte at the beginning of
your structure, whereas a bit-field containing 8 bits might occupy
more than one byte, whatever the implementation's value of CHAR_BIT.

An 8-bit bit field will occupy exactly 8 bits; it can be less than a
byte, but it can't be more.

No, a bit-field occupies the amount of space taken up by the
addressable storage unit that the implementation decides to use to
store it.

And there is nothing in the standard prohibiting a bit-field that does
not use all of the bits in its addressable storage unit from having
its bits non-contiguous.

An application that places an 8-bit bit-field in a 16-bit storage unit
is free to put one of the bits in alternate bits.
 
K

Keith Thompson

Jack Klein said:
[...]
An 8-bit bit field will occupy exactly 8 bits; it can be less than a
byte, but it can't be more.

No, a bit-field occupies the amount of space taken up by the
addressable storage unit that the implementation decides to use to
store it.

Hmm. You may be right, but I think it's more a matter of how it's
described than of what's actually permitted.

My assumption was that an N-bit bit-field occupies exactly N bits.
There may be additional bits within the containing storage unit that
don't contribute to the bit-field's value. I was thinking of those
bits as being a kind of padding *between* members, not as padding bits
within the bit-field. I don't think the standard is clear on which it
is -- but I don't think it needs to be.

C99 6.7.2.1p10:

An implementation may allocate any addressable storage unit
large enough to hold a bit-field. If enough space remains,
a bit-field that immediately follows another bit-field in
a structure shall be packed into adjacent bits of the same
unit. If insufficient space remains, whether a bit-field that
does not fit is put into the next unit or overlaps adjacent
units is implementation-defined. The order of allocation of
bit-fields within a unit (high-order to low-order or low-order
to high-order) is implementation-defined. The alignment of the
addressable storage unit is unspecified.

I think you're interpreting the first sentence to mean that the the
addressable storage unit is allocated *to the bit-field*. That's not
an unreasonable interpretation, but the second sentence, which talks
about possibly allocating the remaining space in that storage unit to
another bit-field, tends to weaken that assumption.

In any case, since you can't apply sizeof to a bit-field, the question
of whether any "padding" bits are part of the bit-field or not isn't
necessarily meaningful.
And there is nothing in the standard prohibiting a bit-field that does
not use all of the bits in its addressable storage unit from having
its bits non-contiguous.

An application that places an 8-bit bit-field in a 16-bit storage unit
is free to put one of the bits in alternate bits.

Perhaps, but I think that would be a perverse implementation.
 
J

Jack Klein

Jack Klein said:
[...]
An 8-bit bit field will occupy exactly 8 bits; it can be less than a
byte, but it can't be more.

No, a bit-field occupies the amount of space taken up by the
addressable storage unit that the implementation decides to use to
store it.

Hmm. You may be right, but I think it's more a matter of how it's
described than of what's actually permitted.

My assumption was that an N-bit bit-field occupies exactly N bits.
There may be additional bits within the containing storage unit that
don't contribute to the bit-field's value. I was thinking of those
bits as being a kind of padding *between* members, not as padding bits
within the bit-field. I don't think the standard is clear on which it
is -- but I don't think it needs to be.

C99 6.7.2.1p10:

An implementation may allocate any addressable storage unit
large enough to hold a bit-field. If enough space remains,
a bit-field that immediately follows another bit-field in
a structure shall be packed into adjacent bits of the same
unit. If insufficient space remains, whether a bit-field that
does not fit is put into the next unit or overlaps adjacent
units is implementation-defined. The order of allocation of
bit-fields within a unit (high-order to low-order or low-order
to high-order) is implementation-defined. The alignment of the
addressable storage unit is unspecified.

I think you're interpreting the first sentence to mean that the the
addressable storage unit is allocated *to the bit-field*. That's not
an unreasonable interpretation, but the second sentence, which talks
about possibly allocating the remaining space in that storage unit to
another bit-field, tends to weaken that assumption.

In any case, since you can't apply sizeof to a bit-field, the question
of whether any "padding" bits are part of the bit-field or not isn't
necessarily meaningful.
And there is nothing in the standard prohibiting a bit-field that does
not use all of the bits in its addressable storage unit from having
its bits non-contiguous.

An application that places an 8-bit bit-field in a 16-bit storage unit
is free to put one of the bits in alternate bits.

Perhaps, but I think that would be a perverse implementation.

The good old DS9000 comes to mind...
 

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,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top