C++中的volatile对象和非volatile成员

10

像这个问题中的一样: 假设我有一小段代码,像这样:

  #include <iostream>
  using namespace std;

  struct foo {
      int a; 
      foo() : a(12) {};
  };  

  int
  main()
  {
      volatile foo x;
      return 0;
  }   

使用g++ -g -O2编译后,发现x的初始化被优化掉了。

然而这个:

  #include <iostream>
  using namespace std;

  struct foo {
      volatile int a; 
      foo() : a(12) {};
  };  

  int
  main()
  {
      volatile foo x;
      return 0;
  }   

调用构造函数。
如果我尝试在代码中使用变量(例如cout<< foo.a << endl;),则汇编输出在两种情况下是相等的。
我的理解是否正确?
第一种情况下,根本没有访问该结构体,因此它被完全优化掉了。
在第二种情况下,结构体的字段被指定为在构建期间也可能更改,因此无论如何都会调用foo()。
补充:
我已经尝试过上述代码的调整:像while(foo.a--);这样的调用按预期方式工作,它实际上发生了而不是在优化期间被删除/替换为结果,因此似乎volatile实际上被继承了,然而构造函数的行为却表现出这种奇怪的(或者至少是一开始意外的)方式。
编辑2:
我已经用clang和MSVC进行了检查,它们的行为与gcc相同。

对我来说看起来像是个bug。"[basic.type.qualifier]/(1.2) 一个volatile对象是volatile T类型的对象,该对象的子对象..." 因此,在两个示例中,foo.a是一个volatile对象,对它的操作是可观察的副作用,不应被优化掉。 - Igor Tandetnik
@IgorTandetnik 我从来没有真正理解过自动变量如何是易观察的。虽然承认这是编译器的错误,但很难看到它在实践中可能会破坏任何东西(也很难看到编译器应该如何应对这种可能性,优化器应该能够优化代码以foo的构造函数而不必单独为作为volatile实例化的foo情况进行优化)。 - M.M
1个回答

2

我的理解是(但我不确定):

C++中的volatile关键字强制编译器不对内存中看似冗余的读写进行优化。例如,如果你有以下代码:

int x = 5;
x = 6;

它不会被改变为:
int x = 6;

这是因为x可能指向内存中的某个地址,而其他人会读取它,而你实际上并没有在程序中读取它(想象一下,通过写入某个内存地址将某个配置发送到微控制器,并且微控制器从该地址读取其配置 - 如果编译器要优化对这个内存的写入操作,那么整个程序就会出错)。
另一个需要记住的事情是,当使用volatile关键字声明类的实例时,其成员继承此关键字(正如Igor Tandetnik在评论中指出的,参考C++标准)。但这并不是全部真相,因为要获得volatile行为,您必须调用已标记为volatile的成员函数-类似于将成员函数标记为const(请参见此处:http://www.devx.com/tips/Tip/13671)。因为据我所知,构造函数和析构函数不能用volatile关键字标记(如Defining volatile class object),您需要稍微更改代码(也许从构造函数中调用volatile成员函数就可以了,但这只是一个猜测)。

Ctor 无法指定 v。虽然我想看到标准更明确地解释这种情况,但似乎在这里是这种情况。顺便说一下,我已经在问题中添加了一些信息。 - alagner
是的,我认为我写过这样的话:“据我所知,构造函数/析构函数不能标记为volatile关键字”。或者你指的是其他东西? - Piotr Smaroń

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接