评估/访问结构

Ale*_*nze 10 c gcc struct volatile

考虑相同代码的两个略有不同的版本:

struct s
{
  int dummy[1];
};

volatile struct s s;

int main(void)
{
  s;
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

struct s
{
  int dummy[16];
};

volatile struct s s;

int main(void)
{
  s;
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

以下是我使用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
Run Code Online (Sandbox Code Playgroud)

_main:
        pushl   %ebp
        movl    %esp, %ebp
        andl    $-16, %esp
        call    ___main
        xorl    %eax, %eax
        leave
        ret

        .comm   _s, 64, 5
Run Code Online (Sandbox Code Playgroud)

请注意s在第二种情况下无法访问.

它是一个编译器错误还是我只是处理C标准的以下声明,而gcc开发人员只是选择了这样一个奇怪的实现定义,并且仍然遵守规则?:

什么构成对具有volatile限定类型的对象的访问是实现定义的.

这种差异的原因是什么?我自然希望整个结构都可以被访问(或者不被访问,我不确定),无论它的大小和内部是什么.

PS在这种情况下,您的编译器(非gcc或更新的gcc)做了什么?(如果这是你要解决的唯一部分,请在评论中回答这最后一个问题,因为这不是被问到的主要问题,而是更多的好奇心问题).

jar*_*itz 4

对于这个问题,C 和 C++ 之间存在差异,这解释了发生了什么。

铿锵-3.4

当将这些代码片段编译为 C++ 时,发出的程序集在任何一种情况下都没有引用 s。事实上,双方都发出了警告:

volatile.c:8:2: warning: expression result unused; assign into a variable to force a volatile load [-Wunused-volatile-lvalue] s;

在 C99 模式下编译时不会发出这些警告。正如这篇博客文章问题评论中的 GCC wiki 条目中提到的,s在此上下文中使用会导致 C 中的左值到右值转换,但在 C++ 中不会。通过检查 C 的 Clang AST 可以确认这一点,因为 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());
}
Run Code Online (Sandbox Code Playgroud)

EnsureDest强制发射堆栈槽,其大小和类型适合表达式。由于不允许优化器删除易失性访问,因此它们在 IR 和输出 asm 中分别保留为标量加载/存储和 memcpy。考虑到上述情况,这是我所期望的行为。

海湾合作委员会-4.8.2

在这里,我观察到与问题中相同的行为。但是,当我将表达式从 更改为 时s;s.dummy;访问权限不会出现在任一版本中。我不像 LLVM 那样熟悉 gcc 的内部结构,所以我无法推测为什么会发生这种情况。但根据上述观察,我认为这是由于不一致而导致的编译器错误。