2020-05-10 更新: 从 GCC 8 开始,编译器支持选项 -Wcast-align=strict,即使在通常允许未对齐内存访问的目标上也会发出警告。启用它可能是个好主意:the compiler is free to optimise code with the assumption that the pointers are aligned, even on targets that would otherwise not care。
在为 Linux i386 或 x86-64 编译时,在为这些系统使用标准 ABI 时,永远不会发出警告。让我解释一下为什么会这样。
首先,让我们看看gcc's documentation 对-Wcast-align 有什么看法:
每当指针被强制转换时发出警告,使得所需的对齐方式
目标增加。例如,如果 char * 被强制转换为
int * 在只能以二或二访问整数的机器上
四字节边界。
英特尔架构在使用通用指令时不需要整数对齐。引用Intel's Basic Architecture manual,第 4.1.1 章 对齐字、双字、四字和双四字:
字、双字和四字在内存中不需要对齐
在自然边界上。词的自然边界,双词,
和四字是偶数地址,地址可整除
被 4 和地址分别被 8 整除。
然而,为了提高程序的性能,数据结构
(尤其是堆栈)应在自然边界上对齐
可能。
因此,尽管强烈建议对齐,但并非绝对必要。但是,该规则有一个例外,您可能已经想到了。 EFLAGS 寄存器的第 18 位称为“对齐检查”位,CR0 寄存器的第 18 位称为“对齐掩码”标志。当它们都设置为 1 时,对未在其“自然边界”对齐的数据(因此,字 2 字节,双字 4 字节,依此类推)的任何内存访问都会导致 #AC,对齐检查异常。如果您想了解更多信息,请查看Intel System Programming Guide。
但是,System V ABI for i386 和 System V ABI for x86-64 都没有指定设置 EFLAGS 中的对齐标志。事实上,i386 ABI 在第 29 页第 3-3 章 Machine Interface 中记录了以下内容:
Intel386 架构不需要所有的数据访问
正确对齐。 (...) 因此,任意数据访问,例如
指针取消引用或引用参数,可能是也可能不是
正确对齐。访问未对齐的数据会慢于
访问正确对齐的数据,否则没有区别。
虽然它也建议:
编译器应分配具有适当的独立数据对象
对齐。
GCC 总是知道它为其编译代码的平台的 ABI,并且 - 在 x86/64 的情况下 - 知道允许未对齐的数据访问这一事实。这就是为什么这样的代码会在没有对齐警告的情况下编译(让我们忘记以下示例中的严格别名规则):
int main(void)
{
char foo[] = "foobar";
int bar = *(int*)(foo + 1);
return 0;
}
如果您尝试使用 ARM 的 gcc 工具链编译此代码,您将收到警告:
daniel@Jurij:/tmp$ arm-linux-gnueabi-gcc -Wcast-align align.c
align.c: In function 'main':
align.c:4:13: warning: cast increases required alignment of target type [-Wcast-align]
int bar = *(int*)(foo + 1);
这是因为在 ARM 中通常最好避免未对齐的访问。我不是 ARM 专家,所以我真的无话可说。
另外,请注意,我写的大部分内容都不适用于 SSE/AVX。