为什么-Wcast-align没有警告在x86上从char*转换为int*?

lin*_*eak 11 c memory-alignment gcc-warning typecasting-operator

我知道gcc有一个选项-Wcast-align,只要指针被转换就会发出警告,以便增加目标所需的对齐.

这是我的计划:

char data[10];
int ptr = *((int *)data);
Run Code Online (Sandbox Code Playgroud)

在我的机器上,数据的对齐要求是1,而ptr是8.

为什么我没有收到警告?

可能是因为我正在为x86编译吗?

Dan*_*zar 15

在为这些系统使用标准ABI时,在编译Linux i386或x86-64时,永远不会发出警告.让我解释一下为什么会这样.

首先,让我们看看gcc的文档有什么说法-Wcast-align:

每当指针被投射时发出警告,以便增加目标的所需对齐.例如,警告是否将a转换char *int *只能以两字节或四字节边界访问整数的 on机器.

使用通用指令时,英特尔架构不需要对齐整数.引自英特尔基本架构手册,第4.1.1章对齐单词,双字,四字和双四字:

单词,双字和四字不需要在自然边界的内存中对齐.单词,双字和四字的自然边界是偶数地址,地址可被4整除,地址可分别被8整除.但是,为了提高程序的性能,数据结构(尤其是堆栈)应尽可能在自然边界上对齐.

因此,尽管强烈建议,但并非绝对必要.但是,该规则有一个例外,您可能已经想到了这一点.EFLAGS寄存器的位18 称为"对齐检查"位,CR0寄存器的位18 称为"对齐掩码"标志.当它们都设置为1,任何存储器访问到未在其"自然边界"对准的数据(因此,2个字节用于词语,4个字节的双字,等等)导致#AC,所述对齐检查异常.如果您想了解更多相关信息,请查看" Intel系统编程指南".

但是,i386System V ABI和x86-64System V ABI都未指定EFLAGS中的对齐标志已设置.事实上,i386 ABI在第29页,第3-3章机器接口上注意到以下内容:

Intel386架构不要求所有数据访问都正确对齐.(...)因此,任意数据访问(例如指针取消引用或引用参数)可能会或可能不会正确对齐.访问未对齐的数据将比访问正确对齐的数据慢,但除此之外没有区别.

虽然它也建议:

编译器应该使用正确的对齐方式分配独立的数据对象.

GCC总是知道它编译代码的平台的ABI,并且 - 在x86/64的情况下 - 意识到允许未对齐的数据访问这一事实.这就是为什么这样的代码会编译而没有关于对齐的警告(让我们忘记以下示例中的严格别名规则):

int main(void)
{
    char foo[] = "foobar";
    int bar = *(int*)(foo + 1);
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果您尝试使用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);
Run Code Online (Sandbox Code Playgroud)

这是因为ARM中通常最好避免使用未对齐访问.我不是ARM专家,所以我真的不能说更多.

此外,请注意我写的大部分内容不适用于SSE/AVX.