2
votes

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
C standard doesn't define pragmas, so pragmas are compiler-specific. In other words - this is not a defined behaviour. - qrdl
@Lundin I think the documentation is quite clear: 1) #pragma pack(n) simply sets the new alignment. (gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/…) - Ctx
@Ctx Yes it clearly changes the alignment but that doesn't answer why it went off by 1 on that online compiler. There's nothing else allocated save for the struct variable, so why does the compiler pick a misaligned address just for the sake of it? - Lundin
@Lundin The compiler does not deliberately choose a misaligned address, in my case the variable b is simply located adjacent to the transactional memory, which happens to end at 404030 <__TMC_END__>. The next address is then used: 404031 <b> - Ctx
@Ctx That's probably the correct answer then: .bss layout on the specific system. Because when I change it to TEST b={1}; and thereby move the variable to .data, the misaligned address goes away. - Lundin

2 Answers

5
votes

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.

2
votes

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.