Why #pragma pack also affects structs' own alignment?

680 Views Asked by At

I noticed that when #pragma pack is used around a struct, the alignment inside of it is not the only one that is affected, but also the alignment of the struct itself changes. Consider the following:

#include <stdio.h>
#include <stdint.h>

#pragma pack(1)
typedef struct _TEST
{
    uint32_t a;
} TEST;
#pragma pack()

volatile uint8_t n;
TEST b;

int main()
{

    printf("Address %lX rem %lu\n", (long unsigned int)&b, (long unsigned int)(&b)%(sizeof(int)));  
    return 0;
}

You can try this code is here: https://onlinegdb.com/BkebdxZEU

The program returned Address 601041 rem 1, which means that the pragma also had an effect of aligned(1) on the struct.

Why is that? Is this a defined behavior?

2

There are 2 best solutions below

2
On BEST ANSWER

The alignment of a structure is affected by the alignment requirements of its members. A structure as a whole is usually aligned to the alignment of its largest member. Because your struct contained a uint32, it would have been aligned to four bytes, had you not invoked the #pragma before.

However, with #pragma pack(1), you force the alignment required for all its members to 1-byte (or no alignment) and hence the structure can now start at any address in memory, not necessarily at a multiple of four bytes.

0
On

First of all, please note that the variable n does not need to be allocated simply because it is volatile. Since your program doesn't refer to this variable, there is no way for the compiler to do anything meaningful with it and it isn't allocated. Removing n from your program yields the same output, so this doesn't explain the "off by 1".

As mentioned in comments, packing of 1 doesn't make any sense for a struct with a single uint32_t member. This pragma does however change the alignment requirement for the struct from 4 to 1. You can test this with C11 _Alignof(TEST).

This in turn means that the compiler is free to allocate the struct at any address it likes. Apparently there is something else with size 1 byte allocated in the same memory segment as your variable on the given system, and so your struct was simply handed the next available address. The "CRT" (start-up code) as well as the standard lib functions may need to allocate variables beyond those explicitly declared by the programmer.

Notably, the misaligned access makes the code slower on many systems, and can cause a program crash on others.