通过指针访问时的结构对齐

时间:2016-04-21 21:43:49

标签: c struct

从字节流(文件,网络等)访问结构时 对齐是什么意思?

例如,我可以理解编译器为什么要填充以下内容 具有额外字节的结构,用于在字地址处对齐int a和short b(4的倍数)。但是,这在访问内存时意味着什么 随机地址通过使用指针?使用 - >运算符生成低效的代码?或者我错过了什么?

typedef struct{
    void*   ptr;  //4 bytes
    char    c1;   //1 byte
    int     a;    //4 bytes
    char    c2;   //1 byte
    short   b;    //2 byte
    char    c3;   //1 byte
} Odd_Struct;     //Minimum needed = 13 bytes, actual (with padding) = 20

unsigned char buffer[128];
Odd_Struct odd_struct;

odd_struct.a = 123456789;
odd_struct.b = 12345;

printf("sizeof(odd_struct): %d\n", sizeof(Odd_Struct));

memcpy(buffer+3, &odd_struct, sizeof(Odd_Struct));

Odd_Struct* testPtr = (Odd_Struct*)(buffer+3);

printf("testPtr->a: %d\n", testPtr->a);
printf("testPtr->b: %d\n", testPtr->b);

输出

sizeof(odd_struct): 20
testPtr->a: 123456789
testPtr->b: 12345

要回答我为什么要这样做:

我打算使用内存非常有限的系统,所以它很诱人 只是将一个字节(unsigned char)指针强制转换为结构指针并访问它 那样。没有额外的内存副本。 I.E.使用字节到位。 这在使用gcc的x86 PC上运行良好。但根据下面的评论,这似乎是一个坏主意。

2 个答案:

答案 0 :(得分:1)

对齐表示实现可能会限制您可以访问或指向某种类型对象的地址。 This page描述了为什么处理器可以进行此限制以提高性能。

您可以通过选中_Alignof(Odd_Struct)来检查类型的对齐要求(自C11起)。

如果这不等于1,则代码(Odd_Struct*)(buffer+3)可能会导致undefined behaviour。它是否确实导致UB取决于buffer+3是否恰好是对齐要求的倍数。

以下代码是正确的(从技术角度来看,它可能存在,但标准意图uintptr_t表现得合理):

int req = _Alignof(Odd_Struct);
if ((uintptr_t)(buffer+3) % req)
    printf("Would be undefined behaviour.\n");
else
{
    Odd_Struct* testPtr = (Odd_Struct*)(buffer+3);

    printf("testPtr->a: %d\n", testPtr->a);
    printf("testPtr->b: %d\n", testPtr->b);
}

理论上,编译器可以检测潜在的未对齐访问并生成不同的汇编代码,以模拟按照您的意图访问值。我不知道任何实际执行此操作的编译器。

通常,编译器将假定访问正确对齐,并仅为该情况生成正确的程序集。然后行为将取决于处理器。例如,通常ARM CPU会导致硬件陷阱进行未对齐访问,而Intel CPU使用较慢的技术在硬件中实现访问,如我之前链接的页面所述。

一旦尝试将未对齐的地址加载到地址寄存器中,某些CPU甚至可能会陷阱或静默加载错误的地址。

要编写健壮的代码,您不应该对未定义的行为如何表现自己做出假设;相反,避免首先使用未定义的行为编写代码。

答案 1 :(得分:0)

感谢EOF的评论,我找到了另外两个类似的问题: Is converting between pointer-to-T, array-of-T and pointer-to-array-of-T ever undefined behaviour?

Unaligned access through reinterpret_cast

此代码有效,因为虽然行为未定义,但我用来测试的x86 PC必须支持未对齐的指令。

但是,此代码不可移植,甚至不能保证与未来版本的gcc一起使用(因为gcc可能会优化指令以包含需要对齐的指令)。

简而言之,这样做是个坏主意,即使这可能是节省几个字节内存的诱人方法。