[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c

Bit-field union bug

Quentin Pope

8/31/2011 9:23:00 PM

On my compiler, the output of "sizeof(foo)" of the following is 8,
instead of 4.

Which indicates that the compiler is aligning the union at
the next int boundary, rather than coalescing it with "proto".

I think this is a bug.


struct s_skip_ind {
unsigned skip_ind:4;
};

struct s_trans_id {
unsigned trans_val:3;
unsigned trans_id:1;
};

struct foo {
unsigned proto:4;
union {
s_skip_ind skip_ind;
s_trans_id trans_id;
};
};
24 Answers

Keith Thompson

8/31/2011 9:34:00 PM

0

Quentin Pope <qp19433@hotmail.NOSPAM.com> writes:
> On my compiler, the output of "sizeof(foo)" of the following is 8,
> instead of 4.
>
> Which indicates that the compiler is aligning the union at
> the next int boundary, rather than coalescing it with "proto".
>
> I think this is a bug.
>
>
> struct s_skip_ind {
> unsigned skip_ind:4;
> };
>
> struct s_trans_id {
> unsigned trans_val:3;
> unsigned trans_id:1;
> };
>
> struct foo {
> unsigned proto:4;
> union {
> s_skip_ind skip_ind;
> s_trans_id trans_id;
> };
> };

Whether it's a bug or not, it's not a violation of the C standard.
Compilers can legally insert arbitrary padding between any two members
of a struct, or after the last one.

I think I've seen some compilers that use the declared type of a bit
field to determine its alignment; for example, given
unsigned char x:1
unsigned short y:1
unsigned int z:1
x, y, and z might be aligned on 1-byte, 2-byte, and 4-byte boundaries,
respectively. I don't think there's any support for this in the
standard, which only requires support for bit fields of types int,
signed int, unsigned int, and (new in C99) _Bool.

--
Keith Thompson (The_Other_Keith) kst-u@mib.org <http://www.ghoti.ne...
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Ian Collins

8/31/2011 9:37:00 PM

0

On 09/ 1/11 09:23 AM, Quentin Pope wrote:
> On my compiler, the output of "sizeof(foo)" of the following is 8,
> instead of 4.
>
> Which indicates that the compiler is aligning the union at
> the next int boundary, rather than coalescing it with "proto".
>
> I think this is a bug.

I don't. Each struct member has to be correctly aligned. There is
nothing in the standard that says adjacent bit field structs should be
coalesced.

> struct s_skip_ind {
> unsigned skip_ind:4;
> };
>
> struct s_trans_id {
> unsigned trans_val:3;
> unsigned trans_id:1;
> };
>
> struct foo {
> unsigned proto:4;
> union {
> s_skip_ind skip_ind;

struct keyword missing.

> s_trans_id trans_id;
> };

Here you have an unnamed structure member, with is isn't standard, so
the compiler could do what it likes, including ignoring it.

> };


--
Ian Collins

jacob navia

8/31/2011 9:39:00 PM

0

Le 31/08/11 23:23, Quentin Pope a écrit :
> On my compiler, the output of "sizeof(foo)" of the following is 8,
> instead of 4.
>
> Which indicates that the compiler is aligning the union at
> the next int boundary, rather than coalescing it with "proto".
>
> I think this is a bug.
>
>
> struct s_skip_ind {
> unsigned skip_ind:4;
> };
>
> struct s_trans_id {
> unsigned trans_val:3;
> unsigned trans_id:1;
> };
>
> struct foo {
> unsigned proto:4;
> union {
> s_skip_ind skip_ind;
> s_trans_id trans_id;
> };
> };

Most compilers have some option to control alignment.

#pragma pack(1) (For lcc-win or Microsoft coompilers)

__attribute__((packed)) (for gcc)

Read your compiler documentation on how to control the alignment.


Joe Pfeiffer

8/31/2011 9:43:00 PM

0

Quentin Pope <qp19433@hotmail.NOSPAM.com> writes:

> On my compiler, the output of "sizeof(foo)" of the following is 8,
> instead of 4.
>
> Which indicates that the compiler is aligning the union at
> the next int boundary, rather than coalescing it with "proto".
>
> I think this is a bug.
>
>
> struct s_skip_ind {
> unsigned skip_ind:4;
> };
>
> struct s_trans_id {
> unsigned trans_val:3;
> unsigned trans_id:1;
> };
>
> struct foo {
> unsigned proto:4;
> union {
> s_skip_ind skip_ind;
> s_trans_id trans_id;
> };
> };

While I would agree with you, the standard disagrees with both of us.
I'm not quite sure what the point of a bitfield is when the compiler is
free to insert arbitrary padding between elements, but that is indeed
the case.

James Kuyper

8/31/2011 9:56:00 PM

0

On 08/31/2011 05:23 PM, Quentin Pope wrote:
> On my compiler, the output of "sizeof(foo)" of the following is 8,
> instead of 4.
>
> Which indicates that the compiler is aligning the union at
> the next int boundary, rather than coalescing it with "proto".
>
> I think this is a bug.
>
>
> struct s_skip_ind {
> unsigned skip_ind:4;
> };
>
> struct s_trans_id {
> unsigned trans_val:3;
> unsigned trans_id:1;
> };
>
> struct foo {
> unsigned proto:4;
> union {
> s_skip_ind skip_ind;
> s_trans_id trans_id;
> };

Ian has already pointed out to you the syntax errors in that declaration.

> };

"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." (6.7.2.1p10)

However, the thing that follows proto is not another bit field, but a
union. The union contains two structs, both structs contain bit fields,
but those bit fields are not members of the same struct that proto is.
Therefore, 6.7.2.1p10 does not apply.

Here's an alternative approach that should cause the fields to be merged
into the same storage unit. However, there's no guarantees about what
size the "storage unit" is. It could have a size of 8.

struct s_skip_ind {
unsigned proto:4;
unsigned skip_ind:4;
};

struct s_trans_id {
unsigned proto:4;
unsigned trans_val:3;
unsigned trans_id:1;
};

union foo {
struct s_skip_ind skip_ind;
struct s_trans_id trans_id;
};


Note that, with this definition, both 'proto's are required to occupy
the same bits (6.5.2.3p5).

Seebs

8/31/2011 9:57:00 PM

0

On 2011-08-31, Joe Pfeiffer <pfeiffer@cs.nmsu.edu> wrote:
> While I would agree with you, the standard disagrees with both of us.
> I'm not quite sure what the point of a bitfield is when the compiler is
> free to insert arbitrary padding between elements, but that is indeed
> the case.

The point is that the compiler is *permitted* to coalesce bitfields. But
not required to, because some systems really don't provide meaningful support
for them.

Basically, bitfields aren't expected to provide an absolutely reliable
mapping onto a series of bits; they're expected to provide a way that you
can hint that you don't need a lot of storage, such that compilers can in
some cases save a few bytes.

-s
--
Copyright 2011, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seeb... <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/...(Scientology) <-- get educated!
I am not speaking for my employer, although they do rent some of my opinions.

James Kuyper

8/31/2011 10:21:00 PM

0

On 08/31/2011 05:57 PM, Seebs wrote:
> On 2011-08-31, Joe Pfeiffer <pfeiffer@cs.nmsu.edu> wrote:
>> While I would agree with you, the standard disagrees with both of us.
>> I'm not quite sure what the point of a bitfield is when the compiler is
>> free to insert arbitrary padding between elements, but that is indeed
>> the case.
>
> The point is that the compiler is *permitted* to coalesce bitfields. But
> not required to, because some systems really don't provide meaningful support
> for them.

Actually, it is required to do so, when adjacent bit-fields in the same
struct fit in a single storage unit (6.7.2.1p10). And since CHAR_BIT is
required to be >=8, these bit-fields, had they been in the same struct,
would have been required to be stored in adjacent bits of the same
storage unit.

> Basically, bitfields aren't expected to provide an absolutely reliable
> mapping onto a series of bits; they're expected to provide a way that you
> can hint that you don't need a lot of storage, such that compilers can in
> some cases save a few bytes.

An implementation has a lot of freedom to allocate bit-fields; far too
much to make any portable use of them for communications outside of a
single program. However, there's not quite as much freedom as you
thought there was.

Scott Fluhrer

8/31/2011 11:02:00 PM

0


"Quentin Pope" <qp19433@hotmail.NOSPAM.com> wrote in message
news:j3m8np$8gv$1@speranza.aioe.org...
> On my compiler, the output of "sizeof(foo)" of the following is 8,
> instead of 4.
>
> Which indicates that the compiler is aligning the union at
> the next int boundary, rather than coalescing it with "proto".
>
> I think this is a bug.

Actually, I don't think that the compiler is allowed to coalesce them. You
made skip_ind and trans_id structures, which means that you are allowed to
have pointers to them, as in:

struct foo example;
struct s_skip_id *p = &example.skip_ind;

You may have no intention of doing so, but the compiler doesn't know that.
Because all pointers must be on byte boundaries, this means that these
structures must be placed on byte boundaries.

>
>
> struct s_skip_ind {
> unsigned skip_ind:4;
> };
>
> struct s_trans_id {
> unsigned trans_val:3;
> unsigned trans_id:1;
> };
>
> struct foo {
> unsigned proto:4;
> union {
> s_skip_ind skip_ind;
> s_trans_id trans_id;
> };
> };


Peter Nilsson

8/31/2011 11:03:00 PM

0

Quentin Pope <qp19...@hotmail.NOSPAM.com> wrote:
> On my compiler, the output of "sizeof(foo)" of the following
> is 8, instead of 4.

Did your compiler issue the required diagnostic?

> Which indicates that the compiler is aligning the union at
> the next int boundary, rather than coalescing it with
> "proto".
>
> I think this is a bug.

Depends whether you're using a conforming C compiler or not.

> struct s_skip_ind {
> unsigned skip_ind:4;
> };
>
> struct s_trans_id {
> unsigned trans_val:3;
> unsigned trans_id:1;
> };
>
> struct foo {
> unsigned proto:4;
> union {
> s_skip_ind skip_ind;
> s_trans_id trans_id;

C doesn't act like C++ when it comes to struct tags.

> };

C doesn't allow unnamed members in structs and unions, with the
exception of zero width bit-fields.

> };

If we replace this with...

struct foo {
unsigned proto:4;
union {
struct s_skip_ind skip_ind;
struct s_trans_id trans_id;
} u;
};

....then you should realise that a conforming C compiler must be
able to support the following...

struct foo f;
union {
struct s_skip_ind skip_ind;
struct s_trans_id trans_id;
} *up = &f.u;

It would make the compiler's life a tad difficult if it had to
support that _and_ coalesce bit-fields in the way you would like.

See James Kuyper's response for an alternative method.

--
Peter

Seebs

9/1/2011 2:45:00 AM

0

On 2011-08-31, James Kuyper <jameskuyper@verizon.net> wrote:
> Actually, it is required to do so, when adjacent bit-fields in the same
> struct fit in a single storage unit (6.7.2.1p10).

Thanks for the correction. I had a vague sense that there were circumstances
where it was mandatory, and I hadn't thought through that the separate
struct declarations were the problem.

In fact, so far as I can tell, it's *not* permissible to coalesce adjacent
structs.

Assume CHAR_BIT is 8.

struct a { int x:4; };
struct b { struct a a1, a2; } example_b;

If struct b coalesced the bit fields, what would the address of example_b.a2
be? The "struct a" object MUST have a size of at least 1, and it has to be
an integer size.

So you have to calculate the padding of the struct a independently, and then
use that padded object as the sub-member in other structs.

(I wonder whether that's true in Plan 9 C of anonymous structure members...)

-s
--
Copyright 2011, all wrongs reversed. Peter Seebach / usenet-nospam@seebs.net
http://www.seeb... <-- lawsuits, religion, and funny pictures
http://en.wikipedia.org/wiki/...(Scientology) <-- get educated!
I am not speaking for my employer, although they do rent some of my opinions.