tags:

views:

889

answers:

15
struct foo { unsigned x:1; } f;
printf("%d\n", (int)sizeof(f.x = 1));

What is the expected output and why? Taking the size of a bitfield lvalue directly isn't allowed. But by using the assignment operator, it seems we can still take the size of a bitfield type.

What is the "size of a bitfield in bytes"? Is it the size of the storage unit holding the bitfield? Is it the number of bits taken up by the bf rounded up to the nearest byte count?

Or is the construct undefined behavior because there is nothing in the standard that answers the above questions? Multiple compilers on the same platform are giving me inconsistent results.

A: 

Wouldn't

(f.x = 1)

be an expression evaluating to true (technically in evaluates to result of the assignment, which is 1/true in this case), and thus,

sizeof( f.x = 1)

is asking for the size of true in terms of how many chars it would take to store it?

I should also add that the Wikipedia article on sizeof is nice. In particular, they say "sizeof is a compile-time operator that returns the size, in multiples of the size of char, of the variable or parenthesized type-specifier that it precedes."

The article also explains that sizeof works on expressions.

Jason Dagit
f.x = 1 evaluates to f.x, not true.
bradtgmurray
We're both right in this example. 1 is a value for true in C :)But, yes, I should edit the post to reflect that minor thinko.
Jason Dagit
You are confusing types with values. The value has nothing to do with the result of sizeof. sizeof(f.x = 0) will give the same result; It's the type that matters, not the value.
I'm in fact cognizant of the difference between types and values. An interesting "feature" of sizeof is that you can ask for the size of a value. By the way, what is the type of 1 in the above program? When C has to guess it will guess int, but maybe it doesn't need to guess?
Jason Dagit
Jason, instead of getting defensive, why can't you admit that your post was not correct or responsive to the question? It was rightly modded down at first (not by me)If you have to ask what the result type of the expression is, then youdidn't read the discussion or even the original question.
It's not defensive, it's a rhetorical question. The OP seemed to be trying to trick sizeof into giving the size of 1 bit wide field by using the fact that assignment evaluates to the result of the assignment. I was pointing out that the compiler doesn't have to be smart enough to see that's 1 bit.
Jason Dagit
Your text about the "size of true" was not relevant and misleading. The Wikipedia link was not relevant. The fact that sizeof "works on expressions" was already known or the question would not have been asked. The assignment trick was never under debate. I'll stop beating the dead horse now...
A: 
sizeof( f.x = 1)

returns 1 as its answer. The sizeof(1) is presumably the size of an integer on the platform you are compiling on, probably either 4 or 8 bytes.

A: 

No, you must be thinking of the == operator, which yields a "boolean" expression of type int in C and indeed bool in C++.

I think the expression will convert the value 1 to the correspondin bitfield type and assign it to the bitfield. The result should also be a bitfield type because there are no hidden promotions or conversions that I can see.

Thus we are effectively getting access to the bitfield type.

No compiler diagnostic is required because "f.x = 1" isn't an lvalue, i.e. it does not designate the bitfield directly. It's just a value of type "unsigned :1".

I'm specifically using "f.x = 1" because "sizeof f.x" takes the size of a bitfield lvalue, which is clearly not allowed.

+1  A: 

Trying to get the size of a bitfield isn't legal, as you have seen. (sizeof returns the size in bytes, which wouldn't make much sense for a bitfield.)

sizeof(f.x = 1) will return the size of the type of the expression. Since C doesn't have a real "bitfield type", the expression (here: an assignment expression) usually gets the type of the bit field's base type, in your example unsigned int, but it is possible for a compiler to use a smaller type internally (in this case probably unsigned char because it's big enough for one bit).

CL
A: 

The

(f.x = 1)

is not an expression, it is an assignment and thus returns the assigned value. In this case, the size of that value depends on the variable, it has been assigned to.

unsigned x:1

has 1 Bit and its sizeof returns 1 byte (8 bit alignment)

If you would use

unsigned x:12

then the sizeof(f.x = 1) would return 2 byte (again because of the 8 bit alignment)

mana
A: 

The sizeof(1) is presumably the size of an integer on the platform you are compiling on, probably either 4 or 8 bytes.

Note that I'm NOT taking sizeof(1), which is effectively sizeof(int). Look close, I'm taking sizeof(f.x = 1), which should effectively be sizeof(bitfield_type).

I'd like to see a reference to something that tells me whether the construct is legal. As an added bonus, it would be nice if it told me what sort of result is expected.

gcc certainly disagrees with the assertion that sizeof(bitfield_type) should be the same as sizeof(int), but only on some platforms.

A: 

Trying to get the size of a bitfield isn't legal, as you have seen. (sizeof returns the size in bytes, which wouldn't make much sense for a bitfield.)

So are you stating that the behavior is undefined, i.e. it has the same degree of legality as "*(int *)0 = 0;", and compilers can choose to fail to handle this sensibly?

That's what I'm trying to find out. Do you assume that it's undefined by omission, or is there something that explicitly declares it as illegal?

A: 

is not an expression, it is an assignment and thus returns the assigned value. In this case, the size of that value depends on the variable, it has been assigned to.

First, it IS an expression containing the assignment operator.

Second, I'm quite aware of what's happening in my example :)

then the sizeof(f.x = 1) would return 2 byte (again because of the 8 bit alignment)

Where did you get this? Is this what happens on a particular compiler that you have tried, or are these semantics stated in the standard? Because I haven't found any such statements. I want to know whether the construct is guaranteed to work at all.

A: 

in this second example, if you would define your struct as a

struct foo { unsigned x:12} f;

and then write a value like 1 into f.x - it uses 2 Bytes because of the alignment. If you do an assignment like

f.x = 1;

and this returns the assigned value. This is quite similar to

int a, b, c;
a = b = c = 1;

where the asignment is evaluated from right to left. c = 1 assigns 1 to the variable c and this asignment returns the assigned value and assigns it to b (and so forth) until 1 is assigned to a

it is equal to

a = ( b = ( c = 1 ) )

in your case, the sizeof gets the size of your asignment, wich is NOT a bitfield, but the variable assigned to it.

sizeof ( f.x = 1)

does not return the bitfields size, but the variable assigment which is a 12 bit representation of the 1 (in my case) and therefore sizeof() returns 2 byte (because of the 8bit aligment)

mana
A: 

Look, I understand full well what I'm doing with the assignment trick.

You are telling me that the size of a bitfield type is rounded up to the cloest byte count, which is one option I listed in the initial question. But you didn't back it up with references.

In particular, I have tried various compilers which give me sizeof(int) instead of sizeof(char) EVEN if I apply this to a bitfield with only has a single bit.

I wouldn't even mind if multiple compilers randomly get to choose their own interpretation of this construct. Certainly bitfield storage allocation is quite implementation-defined.

However, I really do want to know whether the construct is GUARANTEED to work and yield SOME value.

+3  A: 

The C99 Standard (PDF of latest draft) says in section 6.5.3.4 about sizeof constraints:

The sizeof operator shall not be applied to an expression that has function type or an incomplete type, to the parenthesized name of such a type, or to an expression that designates a bit-field member.

This means that applying sizeof to an assignment expression is allowed.

6.5.16.3 says:

The type of an assignment expression is the type of the left operand ...

6.3.1.1.2 says regarding integer promotions:

The following may be used in an expression wherever an int or unsigned int may be used:

  • ...
  • A bit-field of type _Bool, int, signed int, or unsigned int.

If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int.

So, your test program should output the size of an int, i.e., sizeof(int).

Is there any compiler that does not to this?

CL
A: 

On Solaris 10 for SPARC, GCC 4.3.1 gives the answer 1, and Sun compiler (Studio 11) gives the answer 4. That strongly suggests that the behaviour is not defined, so if you value your code, you will avoid using the construct.

Jonathan Leffler
A: 

CL, I've seen your citations before, and agree they're totally relevant, but even after having read them I wasn't sure whether the code is defined.

6.3.1.1.2 says regarding integer promotions:

Yes, but integer promotion rules only apply if a promotion is in fact carried out. I do not think that my example requires a promotion. Likewise if you do

char ch;
sizeof ch;

... then ch also isn't promoted.

I think we are dealing directly with the bitfield type here.

I've also seen gcc output 1 while many other compilers (and even other gcc versions) don't. This doesn't convince me that the code is illegal because the size could just as well be implementation-defined enough to make the result inconsistent across multiple compilers.

However, I'm confused as to whether the code may be undefined because nothing in the standard seems to state how the sizeof bitfield case is handled.

+2  A: 

You are right, integer promotions aren't applied to the operand of sizeof:

The integer promotions are applied only: as part of the usual arithmetic conversions, to certain argument expressions, to the operands of the unary +, -, and ~ operators, and to both operands of the shift operators, as specified by their respective subclauses.

The real question is whether bitfields have their own types.

Joseph Myers told me:

The conclusion from C90 DRs was that bit-fields have their own types, and from C99 DRs was to leave whether they have their own types implementation-defined, and GCC follows the C90 DRs and so the assignment has type int:1 and is not promoted as an operand of sizeof.

This was discussed in Defect Report #315.

To summarize: your code is legal but implementation-defined.

CL
A: 

Thanks CL, I think that is the answer I was looking for!

Without citing which CL answer, this conversational answer will be hard to know which was the right answer.
piCookie
Yep, forgot to click "accept answer".