5

Possible Duplicate:
Why isn't sizeof for a struct equal to the sum of sizeof of each member?

I was trying to understand the concept of bit fields. But I am not able to find why the size of the following structure in CASE III is coming out as 8 bytes.

CASE I:

struct B    
{
    unsigned char c;  // +8 bits
} b;

sizeof(b); // Output: 1 (because unsigned char takes 1 byte on my system)

CASE II:

struct B
{
    unsigned b: 1;
} b;

 sizeof(b); // Output: 4 (because unsigned takes 4 bytes on my system)

CASE III:

struct B
{
    unsigned char c;  // +8 bits
    unsigned b: 1;    // +1 bit
} b;

sizeof(b); // Output: 8 

I don't understand why the output for case III comes as 8. I was expecting 1(char) + 4(unsigned) = 5.

Community
  • 1
  • 1
user1367292
  • 1,029
  • 2
  • 11
  • 13
  • 1
    you're seeing _structure padding_. do a search for these terms on this site and you'll find lots of information – pb2q Oct 06 '12 at 17:19
  • 1
    `unsigned char` takes one byte on *every* system. – Carl Norum Oct 06 '12 at 17:25
  • 1
    @CarlNorum: True, but that doesn't mean that a `struct` with one `char` (such as Case I) takes one byte too. A different compiler could add three bytes padding to the struct. – MSalters Oct 06 '12 at 17:46
  • @MSalters, yes it most certainly could. I'm not sure I understand what you're getting at? – Carl Norum Oct 06 '12 at 17:50

6 Answers6

7

You can check the layout of the struct by using offsetof, but it will be something along the lines of:

struct B
{
    unsigned char c;  // +8 bits
    unsigned char pad[3]; //padding
    unsigned int bint; //your b:1 will be the first byte of this one
} b;

Now, it is obvious that (in a 32-bit arch.) the sizeof(b) will be 8, isn't it?

The question is, why 3 bytes of padding, and not more or less?

The answer is that the offset of a field into a struct has the same alignment requirements as the type of the field itself. In your architecture, integers are 4-byte-aligned, so offsetof(b, bint) must be multiple of 4. It cannot be 0, because there is the c before, so it will be 4. If field bint starts at offset 4 and is 4 bytes long, then the size of the struct is 8.

Another way to look at it is that the alignment requirement of a struct is the biggest of any of its fields, so this B will be 4-byte-aligned (as it is your bit field). But the size of a type must be a multiple of the alignment, 4 is not enough, so it will be 8.

rodrigo
  • 94,151
  • 12
  • 143
  • 190
3

I think you're seeing an alignment effect here.

Many architectures require integers to be stored at addresses in memory that are multiple of the word size.

This is why the char in your third struct is being padded with three more bytes, so that the following unsigned integer starts at an address that is a multiple of the word size.

rodion
  • 6,087
  • 4
  • 24
  • 29
2

Char are by definition a byte. ints are 4 bytes on a 32 bit system. And the struct is being padded the extra 4.

See http://en.wikipedia.org/wiki/Data_structure_alignment#Typical_alignment_of_C_structs_on_x86 for some explanation of padding

Avi
  • 1,231
  • 10
  • 23
  • in the above case3, char is 8 bits(no prob so far). and I have provided only 1 unsigned int bit. That means, the remaining 31 bits will be padded. But still the size should come as --- 8 bits(char) + 1 bit(which i have provided) + 31 bits(due to padding) == 40 bits or 4 bytes. Why it is coming as 8 bytes? – user1367292 Oct 06 '12 at 17:27
  • There is no way to just put a bit in any more architecture. an unsigned int is 4 bytes on your system – Avi Oct 06 '12 at 17:29
1

To keep the accesses to memory aligned the compiler is adding padding if you pack the structure it will no add the padding.

GriffinHeart
  • 450
  • 5
  • 18
1

The alignment and total size of the struct are platform and compiler specific. You cannot not expect straightforward and predictable answers here. Compiler can always have some special idea. For example:

struct B
{
    unsigned b0: 1;    // +1 bit
    unsigned char c;  // +8 bits
    unsigned b1: 1;    // +1 bit
};

Compiler can merge fields b0 and b1 into one integer and may not. It is up to compiler. Some compilers have command line keys that control this, some compilers not. Other example:

struct B
{
    unsigned short c, d, e;
};

It is up to compiler to pack/not pack the fields of this struct (asuming 32 bit platform). Layout of the struct can differ between DEBUG and RELEASE builds.

I would recommend using only the following pattern:

struct B
{
    unsigned b0: 1;
    unsigned b1: 7;
    unsigned b2: 2;
};

When you have sequence of bit fields that share the same type, compiler will put them into one int. Otherwise various aspects can kick in. Also take into account that in a big project you write piece of code and somebody else will write and rewrite the makefile; move your code from one dll into another. At this point compiler flags will be set and changed. 99% chance that those people will have no idea of alignment requirements for your struct. They will not even open your file ever.

Kirill Kobelev
  • 10,252
  • 6
  • 30
  • 51
  • 1
    "Compiler can merge fields b0 and b1 into one integer and may not" - is this so? I think the compiler is required to keep the order of the bit fields. – ysap Mar 12 '17 at 23:54
  • I believe the most important principle is to "**exactly preserve the observable behavior**". Everything outside of this may be not as exact, may differ from compiler to compiler and from platform to platform, etc. It would be not correct to look at the black and white world, where this happens exactly this way and something else another way. A lot of things are in the grey area. – Kirill Kobelev Mar 19 '17 at 16:05
1

I took another look at this and here's what I found.

  1. From the C book, "Almost everything about fields is implementation-dependant."
  2. On my machine:
 struct B {
    unsigned c: 8;
    unsigned b: 1;
}b;
printf("%lu\n", sizeof(b));

print 4 which is a short;

You were mixing bit fields with regular struct elements.

BTW, a bit fields is defined as: "a set of adjacent bits within a sindle implementation-defined storage unit" So, I'm not even sure that the ':8' does what you want. That would seem to not be in the spirit of bit fields (as it's not a bit any more)

Avi
  • 1,231
  • 10
  • 23