0

I have a question regarding the following:

enter image description here

For the int32_t datatype, does it not have to start at address that is a multiple of 4? So for example it can only start at address such as the following: 0x1004, 0x1008, 0x1012, 0x1016,....

So why is it that b can stores the number 0xEF0369BE when EF starts at 0x1007? I understand we need to add 2 bytes of padding after a[2]. But even if we add 2 bytes of padding, b will still start at 0x1007, so wouldn't that makes b not satisfy the requirement of alignment?

I understand that char a[2] can starts anywhere since char has alignment requirement of 1. But int32_t has alignment requirement of 4, so it can only starts at address that is divisible by 4.

I thought I understand about alignment, but somehow I don't think I am now. Could someone explains a bit what is going on here in terms of alignment? and the alignment of the different types inside a Struct.

Barmar
  • 741,623
  • 53
  • 500
  • 612
hodondo
  • 83
  • 1
  • 5
  • That diagram is demonstrating endianness, it's ignoring alignment requirements. – Barmar Sep 09 '22 at 02:52
  • The compiler will normally ensure that structures start at an appropriately aligned address, so the start will never be `0x1003`. – Barmar Sep 09 '22 at 02:53
  • @Barmar if it is ignoring alignment, then why is it that b is not storing the number right after a[2]? should b not stores 0xABCDEF03 or 0x03EFCDBA if this example is ignoring alignment? – hodondo Sep 09 '22 at 03:17
  • That question doesn't look right. On one hand the size is 12 bytes which clearly includes some padding due to alignment requirements. On the other hand the struct is said to start at an odd address. – Gerhardh Sep 09 '22 at 09:02
  • He's ignoring alignment of the start of the structure, not internal alignment within. Basically, he picked an arbitrary starting location, rather than one that the compiler would have used. – Barmar Sep 09 '22 at 15:02

2 Answers2

0

The C standard does not have any rule that int32_t must have any particular alignment. Alignment requirements are implementation-defined. Each C implementation may choose what its alignment requires are (with some constraints and, to conform to the standard, it must document them).

It is odd the example shows two bytes of padding between the a and b members, as these bytes are apparently not needed for padding if int32_t does not require four-byte alignment. The standard allows implementations to insert padding between members even if it is not needed for alignment, but implementations do not normally do that.

Overall, you should not give this too much concern. It is most likely an example that was not fully thought out.

However, one way this can arise is using GCC’s packed attribute:

  • struct myStruct is declared as shown in the example.
  • The C implementation normally requires int32_t to have four-byte alignment, so it lays out the structure with two bytes of padding between members a and b.
  • A further structure is declared:
struct foo
{
    char x[3];
    struct myStruct s;
} __attribute__((__packed__));

This declares foo to be packed, meaning the normal alignment requirement of its members is suppressed, and the members are packed into consecutive bytes with no padding. While this means the struct myStruct member s is put into consecutive bytes after x, internally it retains its original layout. It has to keep its original layout to be a proper struct myStruct even though its alignment requirement is suppressed. For example, if we executed this code:

struct myStruct m = { some initialization };
struct foo f;
memcpy(&f.s, &m, sizeof m);

then we would want the memcpy to reproduce m in f.s.

Compiling and this code:

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


int main(void)
{
    struct myStruct { char a[2]; int32_t b; int16_t c; };

    struct foo
    {
        char x[3];
        struct myStruct s;
    } __attribute__((__packed__));

    struct foo f;

    printf("%p\n", (void *) &f.s.b);
}

in a C implementation that normally requires four-byte alignment for int32_t produces output of “0x7ffee7e729f7” in one run, showing that the b member is at an address that is 3 modulo 4.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • So in your example here, you have defined f to be a struct that won't add any padding in the structure. But then you are saying that even after adding attribute __packed__, internally padding will still be added or not? because you seem to be saying b still not need to satisfy the alignment of divisible by 4? (since you said its address still is 3 modulo 4), so it is still not divisible by 4 in this case. – hodondo Sep 09 '22 at 03:25
  • @hodondo: Because `struct foo` is packed, no padding is added **between** its members. One of its members is a `struct myStruct`. The internal layout of that member is not changed when it is made part of `struct foo`. However, its members no longer have their normal alignment requirements. – Eric Postpischil Sep 09 '22 at 03:44
  • When you say the internal layout of that member (i.e. struct myStruct) is not changed, do you mean the member inside myStruct will have to satisfy the alignment requirement? but the struct foo doesn't have to since the requirement is suppressed by the "attribute packed" options? – hodondo Sep 09 '22 at 03:47
  • @hodondo: No. The layout of the structure is which members are at which offsets from the start of the structure. – Eric Postpischil Sep 09 '22 at 03:55
  • so does it mean the member inside myStruct, and the member inside foo doesn't have to satisfy the alignment requirement now once the option "attribute packed" is added? – hodondo Sep 09 '22 at 04:07
  • @hodondo: Yes, the `packed` attribute removes alignment requirements. – Eric Postpischil Sep 09 '22 at 10:25
0

By default, the compiler aligns the addr of b to 4. If you don't want this auto-alignments, use __attribute__((__packed__)). See the follwing example:

struct myStruct {
    char a[2];
    int32_t b;
    int16_t c;
};
struct myStruct s;

int main(int argc, char *argv[])
{
    printf("Address of s: %p\n", &s);
    printf("Size of s: %zu\n", sizeof(s));
    printf("Address of s.a: %p\n", &s.a);
    printf("Address of s.b: %p\n", &s.b);
    printf("Address of s.c: %p\n", &s.c);
    return 0;
}

Output:

Address of s: 0x601040
Size of s: 12
Address of s.a: 0x601040
Address of s.b: 0x601044
Address of s.c: 0x601048

After adding __attribute__((__packed__)) when defining myStruct:

struct myStruct {
    char a[2];
    int32_t b;
    int16_t c;
} __attribute__((__packed__));

The output of test code as shown above:

Address of s: 0x601040
Size of s: 8
Address of s.a: 0x601040
Address of s.b: 0x601042
Address of s.c: 0x601046
wangloo
  • 27
  • 7
  • but could you explains why the b in my example would holds that number there? like why b will holds the number 0xEF0369BE ? – hodondo Sep 09 '22 at 03:44
  • since as you can see EF actually STARTS at the address 0x1007 – hodondo Sep 09 '22 at 03:49
  • What compiler are you using? In my opinion, I don't think this actually happens by default. – wangloo Sep 09 '22 at 04:07
  • this is an example from my school lecture. I am not sure how they get the conclusion that in Big Endian machine, b will holds the number 0xEF0369BE. Do you see anything wrong here as well if b holds 0xEF0369BE ?? – hodondo Sep 09 '22 at 04:09
  • The largest member of `myStruct` is `b`, size is 4 bytes. So the start addr of `myStruct` should also be 4-byte-alignment. – wangloo Sep 09 '22 at 04:12
  • you can read this answer, may help: https://stackoverflow.com/a/38144117/19670857 – wangloo Sep 09 '22 at 04:16