2

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?

ZenJ
  • 313
  • 3
  • 15
  • 2
    C standard doesn't define pragmas, so pragmas are compiler-specific. In other words - this is not a defined behaviour. – qrdl Feb 24 '20 at 07:43
  • as @qrdl says, the behavior of `#pragma`s is implementation defined - you'll have to consult the documentation for your specific implementation (compiler, platform, ...). – Sander De Dycker Feb 24 '20 at 07:47
  • Where is the problem? Basically with `#pragma pack` you influence the alignement of the adresses of the fields of the structure, so why shouldn't this happen as well to the alignment of the address of the _first_ element of the structure? – Jabberwocky Feb 24 '20 at 07:56
  • There's nothing to pack in that structure — there's only one element, so there are no holes to pack. – Jonathan Leffler Feb 24 '20 at 08:00
  • The OP clearly wonders why they get an odd address offset of 1 then the pragma is there. It doesn't make any sense - and removing the "n" variable has no effect. As we all know, the gcc documentation is quite shaky and will not answer such details. I can't reproduce it on other gcc ports, only on the linked online compiler. – Lundin Feb 24 '20 at 08:50
  • 1
    @Lundin I think the documentation is quite clear: _1) #pragma pack(n) simply sets the new alignment._ (https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html) – Ctx Feb 24 '20 at 08:54
  • btw. can reproduce this with gcc 8.3.0 on x86_64 – Ctx Feb 24 '20 at 09:01
  • 1
    @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 Feb 24 '20 at 09:03
  • 1
    @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 ` – Ctx Feb 24 '20 at 09:07
  • 1
    @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 Feb 24 '20 at 09:12
  • 1
    @Lundin yes, agreed, the compiler does not align or misalign it but simply doesn't care. So anything can happen in this regard. – Ctx Feb 24 '20 at 09:15

2 Answers2

5

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.

th33lf
  • 2,177
  • 11
  • 15
  • 1
    So the behavior would change if we placed the pragmas inside the struct definition? E.g. the members would be aligned on 1 but the struct on 4? – Paul Ogilvie Feb 24 '20 at 10:18
  • @PaulOgilvie I don't think so. The pragma sets the alignment of the structure members in the upcoming definition, not directly of the structure itself. The structure itself gets aligned according to the alignment of its members. – th33lf Feb 25 '20 at 08:33
2

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.

Lundin
  • 195,001
  • 40
  • 254
  • 396