简体   繁体   English

关于 c 中数据结构的 alignment 的混淆

[英]confusion regarding alignment of data struct in c

I have a question regarding the following:我对以下内容有疑问:

在此处输入图像描述

For the int32_t datatype, does it not have to start at address that is a multiple of 4?对于 int32_t 数据类型,它是否不必从 4 的倍数的地址开始? So for example it can only start at address such as the following: 0x1004, 0x1008, 0x1012, 0x1016,....因此,例如它只能从以下地址开始:0x1004, 0x1008, 0x1012, 0x1016,....

So why is it that b can stores the number 0xEF0369BE when EF starts at 0x1007?那么为什么当EF从0x1007开始时b可以存储数字0xEF0369BE呢? I understand we need to add 2 bytes of padding after a[2].我知道我们需要在 a[2] 之后添加 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?但是即使我们添加了2个字节的填充,b仍然会从0x1007开始,那么这不会使b不满足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.我知道 char a[2] 可以从任何地方开始,因为 char 的 alignment 要求为 1。但 int32_t 的 alignment 要求为 4,因此它只能从可被 4 整除的地址开始。

I thought I understand about alignment, but somehow I don't think I am now.我以为我了解 alignment,但不知何故我不认为我现在。 Could someone explains a bit what is going on here in terms of alignment?有人可以根据 alignment 解释一下这里发生了什么吗? and the alignment of the different types inside a Struct.以及 Struct 中不同类型的 alignment。

The C standard does not have any rule that int32_t must have any particular alignment. C 标准没有任何规定int32_t必须具有任何特定的 alignment。 Alignment requirements are implementation-defined. Alignment 要求是实现定义的。 Each C implementation may choose what its alignment requires are (with some constraints and, to conform to the standard, it must document them).每个 C 实现都可以选择其 alignment 要求的内容(有一些限制,并且为了符合标准,它必须记录它们)。

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.奇怪的是,该示例在ab成员之间显示了两个字节的填充,因为如果int32_t不需要四字节 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.该标准允许实现在成员之间插入填充,即使 alignment 不需要,但实现通常不会这样做。

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 :但是,可能出现这种情况的一种方法是使用GCC 的packed属性

  • struct myStruct is declared as shown in the example. struct myStruct的声明如示例所示。
  • 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 . C 实现通常要求int32_t具有四个字节 alignment,因此它在成员ab之间布置了两个字节填充的结构。
  • 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.这声明foo被打包,这意味着对其成员的正常 alignment 要求被抑制,并且成员被打包成没有填充的连续字节。 While this means the struct myStruct member s is put into consecutive bytes after x , internally it retains its original layout.虽然这意味着struct myStruct成员s被放入x之后的连续字节中,但在内部它保留了其原始布局。 It has to keep its original layout to be a proper struct myStruct even though its alignment requirement is suppressed.即使它的 alignment 要求被抑制,它也必须保持其原始布局为正确的struct myStruct 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 fs .那么我们希望memcpyfs中重现m

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. 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.

By default, the compiler aligns the addr of b to 4. If you don't want this auto-alignments, use __attribute__((__packed__)) .默认情况下,编译器将b的 addr 对齐到 4。如果您不想要这种自动对齐,请使用__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: 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 :在定义myStruct时添加__attribute__((__packed__))后:

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

The output of test code as shown above: output的测试代码如上图:

Address of s: 0x601040
Size of s: 8
Address of s.a: 0x601040
Address of s.b: 0x601042
Address of s.c: 0x601046

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM