3

In C it is possible to force a set of bit-fields to start on a new storage unit relative to their predecessors by specifying a zero-width bit field without a name, e.g.

int field1:10;
int :0;
int field2:5;   // will be in a new storage unit

Is there defined behaviour for what happens if two consecutive zero-width fields are declared, e.g.:

int field1:10;
int :0;
int :0;
int field2:5;   // will be in a new storage unit

In looking at the C90 and C99 specifications I cannot see anything that specifies explicitly whether the additional field is simply ignored, or if it might cause a whole additional storage unit to be set aside.

The C99 standard says (§6.7.2.1):

As a special case, a bit-field structure member with a width of 0 indicates that no further bit-field is to be packed into the unit in which the previous bit-field, if any, was placed.

To my reading that's ambiguous - if you treat :0 as a "virtual" bit-field (albeit not taking any storage) then one could read the above as saying that the next :0 cannot be packed into the same (non-storage) as the previous one.

My compiler does appear to ignore the extra, but I'd like to know if that's actually guaranteed per the specification.

Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • What does your compiler tell you? – alk May 16 '16 at 14:03
  • 2
    @alk that's irrelevant at this point - the question is what do the _standards_ tell me (which at this point is very little) – Alnitak May 16 '16 at 14:04
  • Why not to check there? http://port70.net/~nsz/c/c11/n1570.html#3.14p3 – Eugene Sh. May 16 '16 at 14:08
  • 1
    So much about bit fields is actually implementation specific, that I would not expect to find any guidance in Standard. – SergeyA May 16 '16 at 14:09
  • Microsoft has this to say for VS 2015 `Unnamed bit fields cannot be referenced, and their contents at run time are unpredictable. They can be used as "dummy" fields, for alignment purposes. An unnamed bit field whose width is specified as 0 guarantees that storage for the member following it in the struct-declaration-list begins on an int boundary.` https://msdn.microsoft.com/en-us/library/yszfawxh.aspx – Richard Chambers May 16 '16 at 14:16
  • 4
    I think `:0` bit fields will pad zero or more bits to achieve the desired alignment. If it's already aligned, the pad amount is zero. The idea isn't to skip ahead, but to pad the minimal amount to achieve the desired alignment. Once aligned, subsequent `:0` bit fields (with the same size type specifier) should have no effect. – Tom Karzes May 16 '16 at 14:20
  • @TomKarzes that appears to be a valid interpretation, but I fear that the specification is not that definitive. – Alnitak May 16 '16 at 14:20
  • @RichardChambers thanks for the reference - IMHO that's a more rigorous specification than what's actually in the standards. – Alnitak May 16 '16 at 14:25
  • It might be one of those things that was originally "works as implemented" in the old K&R days, and never tightened up in the ANSI specification. I'm not really sure though - I haven't seen the ANSI spec. for this. – Tom Karzes May 16 '16 at 14:26
  • @Alnitak: That's the point. The behavior is implementation-defined, meaning that implementations are supposed to come up with more rigorous specification than the standard. At the same time it means that each implementation might behave differently. – AnT stands with Russia May 16 '16 at 14:26
  • There is no well-defined behavior for bit-fields in general, so what makes you think zero-field bit-fields would be any different? – Lundin May 16 '16 at 14:31
  • @Lundin I agree, I actually think that there is no such guarantee. I'm trying to persuade a colleague ;-) – Alnitak May 16 '16 at 14:32
  • Regarding the general non-portability of bit-fields, [this](http://stackoverflow.com/questions/6043483/why-bit-endianness-is-an-issue-in-bitfields/6044223#6044223) may be of interest. – Lundin May 16 '16 at 14:34
  • @Lundin: Would you imagine that any implementations should have any particular difficulty allowing programmers to write something like `struct foo { uint32_t holder; signed low12 = 12:holder.0; unsigned 20 high20 = 20:holder.12;}` to indicate that "low12" represents the 12 lower bits of "foo" and "high20" represents the upper 20 bits? Such a design would make them massively more useful; the syntax could easily be extended to allow compilers to support items which take pieces from different storage locations, though I'd make that aspect optional since it would complicate some implementations. – supercat May 18 '16 at 15:47
  • @supercat Implementing bit-fields should be trivial stuff. It just that the C standard is way too vague to make them useful currently. They need a complete overhaul indeed. – Lundin May 19 '16 at 06:17
  • @Lundin: What would be needed to make them useful, beyond defining a syntax to control their arrangement? In their present form, the effort required to implement them far outweighs their value, but with a tiny bit more effort the situation could be reversed. – supercat May 19 '16 at 14:44
  • A implementation may choose a storage unit of any sufficient size for a bit-field. Suppose you have bit-fields of size 1, 0, 0, and 1 in this order that together occupy 3 8-bit bytes. Can you distinguish between an implementation that skips a byte-size storage unit between the first and the last bit field, and an implementation that allocates a two byte unit for the first field and skips nothing? – n. m. could be an AI Aug 08 '17 at 06:01

2 Answers2

2

Whether you count the zero length bit field as an actual bit field or not, you can arguably say, that it is always placed into the same packing unit as the field before it. Because that is the packing unit that it affects: It basically fills it up, so that no further bits may be placed in it. And, the virtual field has no effect whatsoever, if the preceding field is already full. On the other hand, the virtual field does not affect the following packing unit in any way, as that remains fully available to be filled with bits.

As such, the ignoring behavior that you observe seems to be mandatory: In either case, the previous field for the second :0 is in the packing unit that contains the last actual field, independent of whether that previous field is considered the last actual field, or the preceding zero length field.

Having said that, this is definitely a wording corner case that has most likely not been anticipated by the people who wrote the standard, and its wording is indeed less than precise, accordingly. As such, I wouldn't put it past some compilers to interpret the wording in another way. So, I would advise against relying on any specific behavior of int :0; int :0;.

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
  • Sounds good aside from if the first bit field was `int :0;` "It basically fills it up, so that no further bits may be placed in it" --> there is no "it" or "field before it" to fill. Yet I like this answer. – chux - Reinstate Monica Aug 04 '17 at 22:25
  • Funny, I got down voted for nearly the same answer. I'm going to point the finger toward you: `language_lawyer = site_standard;` – veganaiZe Aug 06 '17 at 20:23
  • @veganaiZe: I didn't downvote you, and I don't think that your answer deserves it. However, when I read your answer, what bothered me was calling the issue "undefined". Usually, when we deal with undefined behavior, the behavior in question is explicitly declared undefined by the standard itself, which is definitely not the case here. It feels more like a (partial) hole in the wording, which I believe points into a single direction regarding how it is to be interpreted. Note, that my answer argues exclusively with the wording of the standard, so I believe it's a valid language lawyer answer. – cmaster - reinstate monica Aug 07 '17 at 07:10
  • @cmaster As much as I agree with you (on one hand), the standard specifically addresses issues like this -- whether we accept it or not. Consider carefully re-reading my citation. Thank you. – veganaiZe Aug 08 '17 at 05:40
-1

No, it's not guaranteed per the specification.

It's effectively a flag to the compiler for the previous bit-field. It instructs the compiler to pad/align that bit-field by filling it with zeros.

Since :0 is not a bit-field per se, you are redundantly defining that attribute. Since the standard does not specifically address this issue it is "undefined" behavior and you can not expect it to work consistently across various implementations.

For example: One compiler implementation may silently ignore it while another implementation may break compilation with an error.

If a “shall” or “shall not” requirement that appears outside of a constraint is violated the behavior is undefined. Undefined behavior is otherwise indicated in this International Standard by the words “undefined behavior” or by the omission of any explicit definition of behavior. There is no difference in emphasis among these three: they all describe “behavior that is undefined”

~ undefined behavior (§3.16) C90

Community
  • 1
  • 1
veganaiZe
  • 539
  • 5
  • 13
  • @M.M *Thanks!* Corrected. – veganaiZe Aug 04 '17 at 21:36
  • The question was: *"Is there defined behavior for what happens if two consecutive zero-width fields are declared [?]"* --Therefore I stand behind this (corrected) answer. – veganaiZe Sep 13 '17 at 02:39
  • Unclear wording doesn't imply undefined behaviour. The paragraph quoted in the question is attempting to define the behaviour. There will always be situations like this given that the specification is in English and not formal logic or something. – M.M Sep 13 '17 at 05:41
  • The paragraph quoted *does* define the behavior. It *does not* address redundantly applying the attribute in such fashion. If you want an "*empty*" aligned bit field, afterwards, you must define a valid (`:1` or greater) bitfield and then then `:0` again. Therefore it is still **undefined** -- No conforming compiler should implement the imagined behavior mentioned in the question. – veganaiZe Sep 14 '17 at 01:49