11

When you write

struct {
    unsigned a:3, b:2;
} x = {10, 11};

is x.b guaranteed to be 3 by ANSI C (C89)? I have read and reread the standard, but can't seem to find exactly that case.

For example, "result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting unsigned integer type." speaks about computation, not about initialization. And moreover, bit-field is not really a type.

Also, (when speaking about unsigned t:4) "contains values in the range [0,15]", but it doesn't necessarily mean that initializer must be reduced modulo 16 to be mapped to [0,15].

Struct initialization is really painstakingly detailedly described, but I really can't seem to find exactly that behavior. (Of course compilers do exactly that. And IBM documentation says " when you assign a value that is out of range to a bit field, the low-order bit pattern is preserved and the appropriate bits are assigned.", but I'd like to know if ANSI C standardizes that.

Thomas Dickey
  • 51,086
  • 7
  • 70
  • 105
Veky
  • 2,646
  • 1
  • 21
  • 30
  • If one considers bit-field an entity, and interprets the standard literally, then "no initializer shall attempt to provide a value for an object not contained within the entity being initialized" would prohibit such initialization as an attempt to provide a value for bits outside the bit-field. I am not sure that this interpretation is correct or not. In any case, I would avoid this, and use an unambiguous `{10 & 0x07, 11 & 0x03}` initializer instead. Oh, and I agree with you on that the standard is not specific enough on this matter. – Sergey Kalinichenko May 12 '14 at 12:55
  • Of course noone would write this in real code. This is a potential question on a test for a course covering C89. I am arguing it is not a good question, since someone could answer differently and we still couldn't prove he's wrong. – Veky May 12 '14 at 13:04
  • I agree, this would be an awful question for a test, unless you are testing someone who sets out to write a compiler (and even then it would be a very tough question). – Sergey Kalinichenko May 12 '14 at 13:31
  • Just to make sure there's no misunderstanding... no, the question on test is not "is x.b guaranteed to be 3", but just "what will x.b be". They don't have to know every letter of the standard, we just want to cover ourselves if a nitpick comes and says it isn't specified. :-) – Veky May 13 '14 at 05:27

2 Answers2

7

"ANSI C"/C89 has been obsolete for 25 years. Therefore, my answer cites the current C standard ISO 9899:2011, also known as C11.


Pretty much everything related to bit-fields in the C standard is poorly defined. Typically, you will not find anything explicitly addressing the behavior of bit fields, but their behavior is rather specified implicitly, "between the lines". This is why you should avoid using bit fields.

However, I believe that this specific case is well-defined: it should work like any other integer initialization.

The detailed struct initialization rules you mention (6.7.9) show how the literal 11 in the initializer list is related to the variable b. Nothing strange with that. What then applies is "simple assignment", the same thing that would happen as if you wrote x.b = 11;.

When doing any kind of assignment or initialization in C, the right operand is converted to the type of the left operand. This is specified by C11 6.5.16:

In simple assignment (=), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand.

In your case, the literal 11 of type int is converted to a bit field of unsigned int:2.

Therefore, the rule you are looking for should be found in the chapter dealing with conversions (C11 6.3). What applies is what you already cited in your question, C11 6.3.1.3:

...if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.

The maximum value of an unsigned int:2 is 3. One more than the maximum value is 3+1=4. The compiler should repeatedly subtract this from the value 11:

11 - (3+1) = 7    does not fit, subtract once more:
 7 - (3+1) = 3    does fit, store value 3

But then of course, this is the very same thing as taking the 2 least significant bits of the decimal value 11 and storing them in the bit field.

Community
  • 1
  • 1
Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Nice answer, and a nice point. Would you trust any compiler from any source to reliably implement the C89 standard? If it's an old compiler, wouldn't you just try it? And if it's a new compiler, who cares? – david.pfx May 12 '14 at 14:34
  • 2
    See the comment on original question: this isn't about any particular compiler, but about a Platonic entity named "C89". :-) – Veky May 13 '14 at 05:28
2

WRT "speaks about computation, not about initialization", the C89 standard explicitly applies the rules of assignment and conversion to initialization. It also says:

A bit-field is interpreted as an integral type consisting of the specified number of bits.

Given those, while a compiler warning would clearly be in order, it seems that throwing away upper-order bits is guaranteed by the standard.

Sneftel
  • 40,271
  • 12
  • 71
  • 104
  • Ah yes... that sentence evaded me somehow. Thanks. – Veky May 13 '14 at 05:24
  • However, that's obviously a lie when taken literally: sizeof(x.b) is not 2/CHAR_BIT. :-) – Veky May 13 '14 at 05:42
  • @Veky I think a bit field is still a subdivision of `unsigned`. The compiler messes with the bits, not you. If you want to go down to the bit level, you must do the book-keeping yourself, I'm afraid. – U. Windl Oct 23 '20 at 08:46