我知道gcc有一个选项-Wcast-align,只要指针被强制转换就会发出警告,以便增加目标所需的对齐。
这是我的计划:
char data[10];
int ptr = *((int *)data);
在我的机器上,数据的对齐要求为1,而ptr为8。
为什么我没有收到警告?
可能是因为我正在为x86编译吗?
答案 0 :(得分:15)
在为这些系统使用标准ABI时,编译Linux i386或x86-64时,永远不会发出警告。让我解释一下为什么会这样。
首先,让我们看一下gcc's documentation对-Wcast-align
所说的内容:
每当指针被投射时发出警告,以便所需的对齐方式 目标增加了。例如,警告
char *
是否转换为int *
在整数只能以两个或两个访问的机器上 四字节边界。
使用通用指令时,英特尔架构不需要对齐整数。引自Intel's Basic Architecture manual,第4.1.1章字对齐,双字,四字和双四字:
因此,尽管强烈建议,但并非绝对必要。但是,该规则有一个例外,您可能已经考虑过这个规则。单词,双字和四字不需要在内存中对齐 在自然界限上。单词的自然界限,双字, 和四字是偶数地址,地址可以被整除 四分之一,地址可分别被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章机器接口中注意到以下内容:
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。