Question

Consider the following program compiled in VC++ 2010:

#pragma pack(push, 1)    // 1, 2, 4, 8
struct str_test
{
    unsigned int n;
    unsigned short s;
    unsigned char b[4];
};
#pragma pack(pop)

int main()
{
    str_test str;
    str.n = 0x01020304;
    str.s = 0xa1a2;
    str.b[0] = 0xf0;
    str.b[1] = 0xf1;
    str.b[2] = 0xf2;
    str.b[3] = 0xf3;

    unsigned char* p = (unsigned char*)&str;
    std::cout << sizeof(str_test) << std::endl;
    return 0;
}

I set breakpoint on return 0; line and look memory window in the debugger, starting from address p. I get the following results (sizeof and memory layout, depending on pack):

// 1 - 10    (pack, sizeof)
// 04 03 02 01 a2 a1 f0 f1 f2 f3

// 2 - 10
// 04 03 02 01 a2 a1 f0 f1 f2 f3

// 4 - 12
// 04 03 02 01 a2 a1 f0 f1 f2 f3

// 8 - 12
// 04 03 02 01 a2 a1 f0 f1 f2 f3

Two questions:

  1. Why sizeof(str_test) is 12 for pack 8?

  2. Why memory layout is the same and doesn't depend on the pack value?

Was it helpful?

Solution

Why sizeof(str_test) is 12 for pack 8?

From the MSDN docs:

The alignment of a member will be on a boundary that is either a multiple of n or a multiple of the size of the member, whichever is smaller.

In your case the largest member is 4bytes which is smaller than 8, so 4bytes will be used for alignement.

Why memory layout is the same and doesn't depend on the pack value?

The compiler isn't permited to reorder struct members, but can pad the members. In case of pack 8 it does the following;

#pragma pack(push, 8)    // largest element is 4bytes so it will be used instead of 8
struct str_test
{
    unsigned int n; // 4 bytes
    unsigned short s; // 2 bytes        
    unsigned char b[4]; // 4 bytes
    //2 bytes padding here;
};
#pragma pack(pop)

And hence sizeof(str_test) will be 12.

Well, it seems the compiler (MSVC2010) change padding location based on the type, in the case of unsigned char b[4]; it placed two bytes padding at the end of the structure. In your case the 2 bytes cc cc happens to be after the character array.

#pragma pack(push, 8)    // largest element is 4bytes so it will be used instead of 8
struct str_test
{
    unsigned int n; // 4 bytes
    unsigned short s; // 2 bytes 
    //2 bytes padding here;       
    int;  // 4 bytes

};
#pragma pack(pop)

What I did is change the last member from char[4] to int, and can be verified by subtracting the addresses of the last and first member in both cases 6 and 8 respectively.

Memory dump in the case of last member int as follows

04 03 02 01 a2 a1 cc cc f0 f1 f2 f3

Memory dump in the case of last member unsigned char[4] as follows

04 03 02 01 a2 a1 f0 f1 f2 f3 cc cc
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top