Packed bit fields in c structures - GCC
Asked Answered
B

2

27

I am working with structs in c on linux. I started using bit fields and the "packed" attribute and I came across a wierd behavior:

struct __attribute__((packed)) {
    int a:12;
    int b:32;
    int c:4;
} t1;

struct __attribute__((packed))  {
    int a:12;
    int b;
    int c:4;
}t2;

void main()
{
    printf("%d\n",sizeof(t1)); //output - 6
    printf("%d\n",sizeof(t2)); //output - 7
}

How come both structures - that are exactly the same - take diffrent number of bytes?

Bostic answered 13/9, 2014 at 11:19 Comment(2)
Because t2::b is guaranteed to be a distinct memory location? Think of data races.Dipietro
the result of sizeof must be printed using %zuAllseed
K
42

Your structures are not "exactly the same". Your first one has three consecutive bit-fields, the second has one bit-field, an (non bit-field) int, and then a second bit-field.

This is significant: consecutive (non-zero width) bit-fields are merged into a single memory location, while a bit-field followed by a non-bit-field are distinct memory locations.

Your first structure has a single memory location, your second has three. You can take the address of the b member in your second struct, not in your first. Accesses to the b member don't race with accesses the a or c in your second struct, but they do in your first.

Having a non-bit-field (or a zero-length bit-field) right after a bit-field member "closes" it in a sense, what follows will be a different/independent memory location/object. The compiler cannot "pack" your b member inside the bit-field like it does in the first struct.

Kelseykelsi answered 13/9, 2014 at 11:41 Comment(0)
H
30
struct t1 // 6 bytes
{
    int a:12; // 0:11
    int b:32; // 12:43
    int c:4;  // 44:47
}__attribute__((packed));

struct t1 // 7 bytes
{
    int a:12; // 0:11
    int b;    // 16:47
    int c:4;  // 48:51
}__attribute__((packed));

The regular int b must be aligned to a byte boundary. So there is padding before it. If you put c right next to a this padding will no longer be necessary. You should probably do this, as accessing non-byte-aligned integers like int b:32 is slow.

Haroldharolda answered 13/9, 2014 at 11:41 Comment(4)
as accessing non-byte-aligned integers like int b:32 is slow source? define slow. Doesn't it just shift and mask the memory value?Self
It depends, but it will never be as fast as byte alignment. How slow (or even how we define slow) depends on the application, the compiler, etc.Haroldharolda
Understood, my guess is it's just a matter of fast shift+mask.Self
It may just 'shift+mask' if the entire value fits into a single register. But there's a lot of maybes. Maybe it doesn't fit into a single register (platform-dependent) so it's 2 shifts, 2 masks, and an or between 2 registers. Maybe this structure is involved in an operation that could've been vectorized before but can't now (or can be but w/ unaligned loads). Lots of maybes.Solenne

© 2022 - 2024 — McMap. All rights reserved.