Why does this struct not align properly?

531 Views Asked by At

I was reading this answer : C struct memory layout? and was curious to know why :

struct ST
{
   long long ll;
   char ch2;
   char ch1;
   short s;
   int i;
};

still is the size of 24 bytes instead of 16. I was expecting 2*char + short + int to fit into 8 bytes. Why is it so?

EDIT:

Sorry for the confusion, I am running on a 64 bit system (debian) gcc (Debian 4.4.5-8) 4.4.5. I already know its due to padding. My question was why? One of the answers suggests :

char = 1 byte
char = 1 byte
short = 1 byte (why is this 1 and not 2?)
* padding of 5 bytes

My question is, why is this padding here... why not just put an int straight after the short, it will still fit within 8 bytes.

3

There are 3 best solutions below

0
On BEST ANSWER

The simple answer is: it isn't 24 bytes. Or you're running on on the 64 bit s390 port of Linux that I haven't been able to find the ABI documentation for. Every other 64 bit hardware that Debian can run on will have the size of this struct as 16 bytes.

I have dug up the ABI documentation for a bunch of different CPU ABIs and they all have more or less this wording (it seems they have all been copying from each other):

Structures and unions assume the alignment of their most strictly aligned component. Each member is assigned to the lowest available offset with the appropriate alignment. The size of any object is always a multiple of the object's alignment.

And all architecture ABI documents I found (mips64, ppc64, amd64, ia64, sparc64, arm64) have the alignment for char 1, short 2, int 4 and long long 8.

Even though operating systems are allowed to make their own ABI, almost every unix-like system and especially Linux follow the System V ABI and their supplemental CPU documentation that specifies this behavior very well. And Debian will definitely not change this behavior to be different from all other Linuxes.

Here's a quick verification (all on amd64/x86_64 which is what you're most likely running):

$ cat > foo.c
#include <stdio.h>

int
main(int argc, char **argv)
{
    struct {
        long long ll;
        char ch2;
        char ch1;
        short s;
        int i;
    } foo;

    printf("%d\n", (int)sizeof(foo));
    return 0;
}

MacOS:

$ cc -o foo foo.c && ./foo && uname -ms
16
Darwin x86_64

Ubuntu:

$ cc -o foo foo.c && ./foo && uname -ms
16
Linux x86_64

CentOS:

$ cc -o foo foo.c && ./foo && uname -ms
16
Linux x86_64

OpenBSD:

$ cc -o foo foo.c && ./foo && uname -ms
16
OpenBSD amd64

There's something else wrong with your compilation. Or that's not the struct you're testing or you're running on a very strange hardware architecture and specifying it as "64 bit" is equivalent to saying "I'm driving this government issued vehicle and it has very strange acceleration and the engine cuts out after 5 minutes" and not mentioning that you're talking about the space shuttle.

0
On

Usually to minimize paddings, it's normal practice to order the members from biggest to smallest, could you try to reorder them and see what comes out of this?

struct ST
{
   long long ll;  //8 bytes
   int i;         //4bytes
   short s;       //2 bytes
   char ch2;      //1 byte
   char ch1;      //1 byte
};

total is 16 bytes

1
On

It's all about padding. In visual studio (and some other compilers) you can use the #pragma push/pack to make it align as you wish.

#pragma pack(push, 1)
struct ST
{
   /*0x00*/ long long ll;
   /*0x08*/ char ch2;
   /*0x09*/ char ch1;
   /*0x0a*/ short s;
   /*0x0c*/ int i;
   /*0x10*/
};
#pragma pack(pop)

Since you said it was coming up as size 24, I'm going to guess that the compiler is aligning you at 4 bytes by doing something like this:

struct ST
{
   /*0x00*/ long long ll;
   /*0x08*/ char ch2;
   /*0x09*/ char padding1[0x3];
   /*0x0c*/ char ch1;
   /*0x0d*/ char padding2[0x3];
   /*0x10*/ short s;
   /*0x11*/ char padding3[0x2];
   /*0x14*/ int i;
   /*0x18*/
};

(Sorry, I think in hex when doing this sort of thing. 0x10 is 16 in decimal and 0x18 is 24 in decimal.)