Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why would the type of a bit field affect the size of the containing structure?

Tags:

c

gcc

struct

First, here's what the ISO C standard says about bit fields, quoting the N1570 draft of the 2011 ISO C Standard, section 6.7.2.1:

A bit-field shall have a type that is a qualified or unqualified version of _Bool, signed int, unsigned int, or some other implementation-defined type. It is implementation-defined whether atomic types are permitted.

...

A bit-field is interpreted as having a signed or unsigned integer type consisting of the specified number of bits. If the value 0 or 1 is stored into a nonzero-width bit-field of type _Bool, the value of the bit-field shall compare equal to the value stored; a _Bool bit-field has the semantics of a _Bool.

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. If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified.

For any struct type, the alignment of the type is at least the maximum alignment of any member of the type, and the size of any type is a multiple of its alignment. For example, if a structure contains a (non-bit-field) int member, and int requires 4-byte alignment, then the structure itself requires 4-byte alignment or more.

Many compilers permit bit fields of integer types other than _Bool and the int types.

For at least some compilers, the alignment of a struct containing a bit field is at least the alignment of the declared type of the bit field. For example, for gcc 4.7.2 on x86_64, given:

struct sb {
    _Bool bf:1;
};
struct si {
    unsigned bf:1;
};

gcc gives struct sb a size and alignment of 1 byte (which is the size and alignment of _Bool), and struct si a size and alignment of 4 bytes (which is the size and alignment of int). It does the same thing with bit fields of implementation-defined types; a bit field defined as long long bf:1; forces an 8-byte size and alignment for the enclosing structure. This is done even though in both cases the bit field bf is an object whose width is just 1 bit.

I've seen similar behavior with Sun's compiler on SPARC/Solaris 9.

Experiment shows that multiple bit fields defined either as _Bool or as unsigned can be packed into adjacent bits within a single byte (in fact that's required), so the bit fields themselves do not have strict alignment requirements.

I understand that the layout of struct members is largely implementation-defined, and I don't believe that gcc's behavior violates the C standard.

So my question (finally!) is, why does gcc (along with at least one unrelated C compiler, and probably more) do this? Do the authors of gcc assume that the declared type of a bit field must affect the size and alignment of the containing struct? Are they correct in this assumption? Is there a requirement in the C standard itself that I've missed?

Here's a test program that exhibits the behavior. If you want to run it on your system, you might need to comment out parts of it, if you're using an old compiler that doesn't support some of the newer features, or one that doesn't permit certain types of bit fields. I'd be interested in knowing if there are compilers that don't behave as gcc does.

#include <stdio.h>
#include <limits.h>
#include <stdint.h>
int main(void) {
    struct sb  { _Bool    bf:1; };
    struct s8  { uint8_t  bf:1; };
    struct s16 { uint16_t bf:1; };
    struct s32 { uint32_t bf:1; };
    struct s64 { uint64_t bf:1; };
    printf("sizeof (struct sb)  = %2zu (%2zu bits)\n",
           sizeof (struct sb),
           sizeof (struct sb)  * CHAR_BIT);
    printf("sizeof (struct s8)  = %2zu (%2zu bits)\n",
           sizeof (struct s8),
           sizeof (struct s8)  * CHAR_BIT);
    printf("sizeof (struct s16) = %2zu (%2zu bits)\n",
           sizeof (struct s16),
           sizeof (struct s16) * CHAR_BIT);
    printf("sizeof (struct s32) = %2zu (%2zu bits)\n",
           sizeof (struct s32),
           sizeof (struct s32) * CHAR_BIT);
    printf("sizeof (struct s64) = %2zu (%2zu bits)\n",
           sizeof (struct s64),
           sizeof (struct s64) * CHAR_BIT);
    return 0;
}

Here's the output I get on my system:

sizeof (struct sb)  =  1 ( 8 bits)
sizeof (struct s8)  =  1 ( 8 bits)
sizeof (struct s16) =  2 (16 bits)
sizeof (struct s32) =  4 (32 bits)
sizeof (struct s64) =  8 (64 bits)
like image 910
Keith Thompson Avatar asked Apr 12 '13 20:04

Keith Thompson


1 Answers

In a way you've answered the question yourself with this quotation from the standard:

The alignment of the addressable storage unit is unspecified.

The compiler can choose any alignment and adhere to the C standard, but that's not the whole story.

In order for code compiled with different compilers to interoperate, the platform ABI must specify these details. For example the SYS-V i386 ABI used by Linux x86 says:

Bit-fields obey the same size and alignment rules as other structure and union members, with the following additions: [...]

  • A bit-field must entirely reside in a storage unit appropriate for its declared type.

It then follows that regardless of the width, a long bitfield must reside in something that is aligned on a 4 byte boundary.

like image 106
Geoff Reedy Avatar answered Oct 08 '22 17:10

Geoff Reedy