tags:

views:

391

answers:

8

Hello

Consider following example:

#include <stdio.h>
#include <inttypes.h>

struct A {
        uint32_t i1;
        uint32_t i2;
        uint32_t i3;
        uint64_t i4;
        uint32_t i5;
        uint32_t i6;
        uint32_t i7;
        uint64_t i8;
        uint32_t i9;
};

struct B {
        uint32_t i1;
        uint32_t i2;
        uint32_t i3;
        uint32_t i4;
        uint32_t i5;
        uint32_t i6;
        uint32_t i7;
        uint64_t i8;
        uint64_t i9;
};

int
main()
{
        struct A a;
        struct B b;

        printf("sizeof(a) = %u, sizeof(b) = %u\n", sizeof(a), sizeof(b));

        return 0;
}

Output is:

$ ./t2 
sizeof(a) = 56, sizeof(b) = 48
$

Why are they differ on 64bit machine ? On 32 bit platform results are the same:

$ ./t2
sizeof(a) = 44, sizeof(b) = 44
+8  A: 

The compiler aligns the struct members by a boundary (which is different in your compilation attempts).

Add a

#pragma pack (1)

directive at the beginning of source file and retry.

Mehrdad Afshari
oh i get it, for a then:32, 32, 32, <hidden 32>, 64, 32, 32, 32, <hidden 32>, 64, 32, <hidden 32>
ryansstack
If you want to know how the elements are arranged, output the pointers for each of them (printf("%p", A.i1); and so on).
schnaader
Matt Kane
#pragma pack(1) is MSVC. For GCC, use __attribute__((packed)).
ephemient
New gcc versions support #pragma pack.
Mehrdad Afshari
It's not enabled on all ports and architectures. I would also argue that making packing a global switch (unless you're careful with push and pop) is a bad idea.
ephemient
None of them are standard nevertheless. Anyway, I wasn't answering a question about how to control alignment, I was just demonstrating the effect of alignment and wrote that as a kind of proof that you could test and see that it's the thing that makes this happen.
Mehrdad Afshari
+1  A: 

Because of the padding between the elements.

Shmoopty
A: 

sizeof(b) is 48 because the last uint32 takes up a full 64-bits (because the subsequent uint64s are aligned to 64-bit blocks. sizeof(a) takes up more because the first 3 unit32s take up 2 blocks, the next 3 take up 2 blocks, and the final uint32 takes a full 64-bit block

Jimmy
+2  A: 

64-bit integers have to be placed on a 64-bit memory boundary. Thus, when creating a struct A on a 64-bit machine, the compiler sticks a 4-byte padding space after i3 and i7 - thus putting an extra 8 bytes in there.

Aric TenEyck
They don't _have_ to be placed on 64-bit boundaries, but you get better performance that way.
Adam Rosenfield
Depends on the processor. Not all 64 bit processors have 64 bit data bus. Some require aligned accesses (like PowerPC and some x86 SSE instructions) and some just benefit from performance improvement. It's really very platform-specific. Either case, at C level, it's not a requirement. Even if you're compiling for a processor that requires aligned memory accesses, the compiler would should generate necessary shift instructions to accomplish the same task.
Mehrdad Afshari
A: 

This is because of aligning.

It is possible that 64bit integers on your platform are required to be 64bit aligned.

So In the mixed structure you have 3 32 bit integer, after them there must be inserted an other 32bit padding to have the 64bit integer correctly aligned.

The size difference should vanish if you insert and even number of 32bit field before your 64 bit field.

Fionn
+1  A: 

This is caused due to structure aligning: struct A has 3 32 bit values followed by a 64bit one. Regardless of the packing of the first 3 elements the 64bit element definitely won't start between boundaries (i.e. taking up half of two separate 64bit values) on 64bit, so there is at least a 32bit padding between the 3rd and 4th element.

bluebrother
+14  A: 

Some diagrams to help you see:

32-bit:

+----+----+----+----+----+----+----+----+----+----+----+
| i1 | i2 | i3 |   i4    | i5 | i6 | i7 |   i8    | i9 | Struct A
+----+----+----+----+----+----+----+----+----+----+----+

+----+----+----+----+----+----+----+----+----+----+----+
| i1 | i2 | i3 | i4 | i5 | i6 | i7 |   i8    |   i9    | Struct B
+----+----+----+----+----+----+----+----+----+----+----+

64-bit:

+---------+---------+---------+---------+---------+---------+---------+
| i1 | i2 | i3 |~~~~|    i4   | i5 | i6 | i7 |~~~~|   i8    | i9 |~~~~| Struct A
+---------+---------+---------+---------+---------+---------+---------+

+---------+---------+---------+---------+---------+---------+
| i1 | i2 | i3 | i4 | i5 | i6 | i7 |~~~~|   i8    |   i9    | Struct B
+---------+---------+---------+---------+---------+---------+
  • + : address boundaries
  • ~ : padding
Juliano
+2  A: 

Because it can. The compiler isn't required to use the same layout between 32 and 64-bit mode. It can insert padding when it wants to. You shouldn't rely on the precise layout of the struct in the first place.

In principle, it could even change the padding each time you compile. (It's hard to imagine why the compiler would do this, but it's allowed to)

jalf
If it did change the padding each time you compiled, you'd never be able to link anything without massive segfaults at runtime...
Greg Rogers
The C standard doesn't say anything about the linker, though. That's an implementation detail. ;)Of course you're right, in practice it'd cause massive problems. My point is just that you shouldn't make assumption about the size or layout of datatypes, even within the same compiler and platform.
jalf