考虑相同代码的两个略有不同的版本:
struct s
{
int dummy[1];
};
volatile struct s s;
int main(void)
{
s;
return 0;
}
和
struct s
{
int dummy[16];
};
volatile struct s s;
int main(void)
{
s;
return 0;
}
以下是我为gcc 4.6.2获得的内容:
_main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
call ___main
movl _s, %eax
xorl %eax, %eax
leave
ret
.comm _s, 4, 2
和
_main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
call ___main
xorl %eax, %eax
leave
ret
.comm _s, 64, 5
请注意在第二种情况下无法访问s
。
这是一个编译器错误还是我只是处理C标准的以下声明而gcc开发人员只是选择了这样一个奇怪的实现定义并且仍然按规则播放?:
对具有volatile限定类型的对象的访问构成是实现定义的。
造成这种差异的原因是什么?我自然希望整个结构被访问(或者不被访问,我不确定),无论其大小和内部结构如何。
P.S。在这种情况下,您的编译器(非gcc或更新的gcc)做了什么? (请在评论中回答最后一个问题,如果这是你要解决的唯一部分,因为这不是被问到的主要问题,而是更多的好奇心问题。)
答案 0 :(得分:3)
这个问题的C和C ++之间存在差异,这解释了正在发生的事情。
当将这些片段中的任何一个编译为C ++时,在任何一种情况下,发出的程序集都不引用s。事实上,两者都发出了警告:
volatile.c:8:2: warning: expression result unused; assign into a variable to force a volatile load [-Wunused-volatile-lvalue]
s;
在C99模式下编译时未发出这些警告。正如this blog post和this GCC wiki entry from the question comments中所述,在此上下文中使用s
导致在C中进行左值到右值的转换,但在C ++中则不然。这是通过检查Clang AST for C来确认的,因为有一个来自LvalueToRValue的ImplicitCastExpr,它在从C ++生成的AST中不存在。 (AST不受结构大小的影响。)
Clang源的快速grep揭示了聚合表达式的发射:
case CK_LValueToRValue:
// If we're loading from a volatile type, force the destination
// into existence.
if (E->getSubExpr()->getType().isVolatileQualified()) {
EnsureDest(E->getType());
return Visit(E->getSubExpr());
}
EnsureDest
强制发出一个堆栈槽,其大小和类型为表达式。由于优化器不允许删除易失性访问,因此它们分别在IR和输出asm中保留为标量加载/存储和memcpy。鉴于上述情况,这是我期望的行为。
在这里,我观察到与问题中相同的行为。但是,当我将表达式从s;
更改为s.dummy;
时,访问权限不会出现在任一版本中。我不熟悉gcc的内部结构,因为我和LLVM一样,所以我不能推测为什么会发生这种情况。但基于上述观察,我认为由于不一致,这是一个编译器错误。