简体   繁体   中英

Sizeof of packed structure with bit fields

Consider this code:

#include <stdio.h>
#include <stdint.h>

#ifdef __GNUC__
#define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__))
#endif

#ifdef _MSC_VER
#define PACK( __Declaration__ ) __pragma(pack(push, 1)) __Declaration__ __pragma(pack(pop))
#endif

PACK(struct S
{
  uint8_t  f0;
  uint32_t f1;
  uint32_t f2 : 17;
});

int main()
{
    printf("%zu\n", sizeof(struct S));
    return 0;
}

No compiler options were given.

Output:

gcc   (9.2.0): 8
clang (8.0.1): 8
cl    (19.23.28106.4 for x86): 9

Why in case of cl result of sizeof is 9?

What the standard says?

The C standard C17 6.7.2.1/11 says, emphasis mine:

An implementation may allocate any addressable storage unit large enough to hold a bitfield. 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.

These "storage units" are only known internally by the compiler and can have different size on different compilers. The concept of bit/byte "packing" is not supported by the standard and also behaves differently from compiler to compiler.

None of the compilers used in your test violates the C standard, since this is 100% implementation-defined behavior.

The requests for packing request that the compiler pack the members into the structures, with no padding. However, they do not change the types or representations of the members themselves. It appears GCC and Clang use three bytes to represent a bit-field of 17 bits and Microsoft uses four, at least when the base type is uint32_t . Thus, for this structure, Clang and GCC pack a one-byte object, a four-byte object, and a three-byte object into eight bytes, while Microsoft packs a one-byte object, a four-byte object, and a four-byte object into nine bytes.

This may be related to the fact that Microsoft's compiler is primarily a C++ compiler and C and C++ treat the types of bit-fields differently . In C, the type of a bit-field may be implementation-defined (the standard is not entirely clear). In C++, the type of a bit-field is its base type.

We can test this by considering a rearrange unpacked structure:

struct S
{
    uint8_t  f0;
    uint32_t f2 : 17;
    uint32_t f1;
};

GCC and Clang show sizeof(struct S) to be eight bytes, which is consistent with a three-byte representation for the bit-field. MSVC shows twelve bytes, which is consistent with a four-byte representation for the bit-field.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM