26

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?

Alexis
  • 2,136
  • 2
  • 19
  • 47
Danny Cohen
  • 505
  • 1
  • 5
  • 11

2 Answers2

41

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.

EnzoR
  • 3,107
  • 2
  • 22
  • 25
Mat
  • 202,337
  • 40
  • 393
  • 406
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.

John Zwinck
  • 239,568
  • 38
  • 324
  • 436
  • `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? – Alexis Jul 07 '21 at 11:22
  • 1
    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. – John Zwinck Jul 08 '21 at 11:14
  • Understood, my guess is it's just a matter of fast shift+mask. – Alexis Jul 08 '21 at 15:33
  • 1
    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. – Matthew M. Jun 24 '22 at 14:39