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

问题描述 投票:11回答:1

我知道gcc有一个选项-Wcast-align,它在投射指针时发出警告,从而增加了目标的所需对齐方式。

这是我的程序:

char data[10];
int ptr = *((int *)data);

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

为什么我没有收到警告?

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

c memory-alignment gcc-warning typecasting-operator
1个回答
15
投票

Update 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


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

首先,让我们看看gcc's documentation-Wcast-align的评价:

警告,只要强制转换指针,以使所需的对齐方式目标提高了。例如,警告是否将char *强制转换为在只能以2或2访问整数的机器上为int *四字节边界。

使用通用指令时,英特尔架构不需要整数对齐。引自Intel's Basic Architecture manual,第4.1.1章单词,双字,四字和双四字的对齐

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

因此,虽然强烈建议对齐,但并非绝对必要。但是,您可能已经想到了该规则的一个例外。 EFLAGS寄存器的位18被称为“对齐检查”位,CR0寄存器的位18被称为“对齐掩码”标志。当它们都设置为1时,任何内存访问未在其“自然边界”处对齐的数据(因此,单词2字节,双字4字节,依此类推)将导致#ACAlignment检查例外。如果您想了解更多有关此的信息,请查看Intel System Programming Guide

但是,System V ABI for i386System 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;
}

如果尝试使用gcc工具链为ARM编译此代码,则会收到警告:

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。

© www.soinside.com 2019 - 2024. All rights reserved.