12

Is the following code guaranteed to terminate normally and successfully?

#include <assert.h>

struct foo_s {
    union {
        struct {
            unsigned a : 10;
            unsigned   : 6;
        };
        struct {
            unsigned   : 10;
            unsigned b : 6;
        };
        struct {
            unsigned   : 10;
            unsigned c : 6;
        };
    };
};

int main () {
    struct foo_s f;
    f.a = 0;
    f.b = 1;
    assert(f.a == 0);
    return 0;
}

While answering a different question, the possibility was raised that assignment to a named bit-field in a structure that also contains an unnamed bit-field may cause arbitrary data to be written to those bits. C.11 §6.7.2.1 ¶12 states:

A bit-field declaration with no declarator, but only a colon and a width, indicates an unnamed bit-field.

My reading of this is that an unnamed bit-field is just a regular bit-field, with the only difference being the value in those bits cannot be obtained directly by name. Is an implementation allowed to extrapolate from that using "as-if" logic and assign arbitrary data in those bits?

Community
  • 1
  • 1
jxh
  • 69,070
  • 8
  • 110
  • 193
  • There's an awful lot of 'implementation defined' behaviour for just about everything to do with bit fields. Why do you need to take a chance? Why not just have a single structure containing no unnamed bit fields? Of course, assigning to one part of a union and reading from another is ill defined too (and the references to `.a` and `.b` are references to different (anonymous) members of the union. – Jonathan Leffler Sep 06 '13 at 06:17
  • Contents of Unanamed bit field at run time are unpredictable so **NO** there is **no guaranteed to terminate normally and successfully**. – Dayal rai Sep 06 '13 at 06:21
  • @JonathanLeffler: The normal reasons to use bit-fields are all based on defining a higher level interface over low level things (like fields in protocol headers). This can be useful not just in code but when inspecting data in the debugger. But, I'd like to put aside that issue, and focus on whether or not the semantics are well-defined. – jxh Sep 06 '13 at 06:28
  • @Dayalrai: I agree if the bits are never initialized. But if the bits are initialized (I give one way, but another way would be with `memset()`), are the bits still unpredictable? – jxh Sep 06 '13 at 06:29
  • 1
    @jxh I see the quagmire. C.11 §6.7.2.1 ¶12 note 126 says "An unnamed bit-field structure member is useful for _padding_ to conform to externally imposed layouts." If an implementation viewed this as true _padding_, then the `f.b = 1` may mess-up `f.a`. OTH "There may be unnamed padding within a structure object, but _not at its beginning_." So it looks like your assertion "unnamed bit-field is just a regular bit-field" has creditability. (I've not used nor regularly seen unnamed bit fields so no experience. I give them dummy names.) – chux - Reinstate Monica Sep 06 '13 at 06:42

1 Answers1

7

Yes, I think an implementation may write arbitrary bits to an unnamed bit field. I think footnote 126 merely states the intention why unnamed bitfields of width larger than 0 were introduced:

An unnamed bit-field structure member is useful for padding to conform to externally imposed layouts.

So basically unnamed bit-fields have the same semantic as padding bits. You just can't rely upon their contents.

Allowing the implementation to basically ignore an unnamed bit-field when writing to an adjacent named bit-field a greatly eases the handling of that field a. The current value of the unnamed field doesn't have to be read and the write can be done atomically in one go. Just as for padding bits that might be contained in the structure.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • The standard seems very specific about where it uses the term *padding bits*, and it is never in the context of bit-fields. The footnote reads more like a description of a use-case rather than imposing a semantic. Typically, an "externally imposed layout" has "reserved for future use" bits, and reserved bits are typically supposed to be always 0 (some hardware will check those bits on the wire), so the unnamed bit-field both conforms to the layout as well as prevent a programmer from accidentally using those bits. That all goes ka-ka if the compiler gets to set random values there. – jxh Sep 06 '13 at 21:36
  • @jxh, I think you are interpreting more into "externally imposed layout" than there is. It just talks about paddidng, not about reserved bits for future use. If an application has a use for these bits, and be it just to ensure that they are set to `0`, it should just name the bit-field. – Jens Gustedt Sep 06 '13 at 22:37
  • My primary point is that the term *unnamed bit-field* shows up twice in the standard (where the term is defined, and description of use in the footnote). It never shows up near the term "padding bits", and "padding bits" are never applied to bit-fields. OTOH, "bit-fields" are described in detail, and given the way the term "unnamed bit-field" is defined, it seems all descriptions of a "bit-field" apply equally to "unnamed bit-field". – jxh Sep 06 '13 at 22:43
  • @jxh: The term itself shows up in another example (broken over two lines) and unnamed members of structures are mentioned particularly in 6.7.9 "Initialization". It explicitly states that unnamed members don't take part in initialization and have indeterminate values. Also Annex J.2 mentions access to unnamed members that makes the behavior undefined. – Jens Gustedt Sep 06 '13 at 23:38
  • I missed that example, thanks. As part of a union, those unnamed parts have names in an alternate structure within the union that for a common initial sequence. The definition of the common initial sequence makes an explicit exception for allowing a structure in the union to inspect a compatible part of a different structure. – jxh Sep 07 '13 at 00:09
  • 1
    So, what I got after bringing this up with `comp.std.c` is that although the language in the standard does leave things undefined, the de factor behavior is what counts in this case because a "compliant" compiler that deviates drastically from behavior expected by the programmer on something as basic as protocol buffers would spell doom for the compiler vendor. – jxh Jul 25 '14 at 16:10