C ++标准对间接运算符保证内存写入的描述是不是已经优化了?

时间:2012-12-06 13:35:37

标签: c++ optimization compiler-construction compiler-optimization indirection

这基本上是this question的延续。到目前为止看起来如果我有这样的函数:

void SecureZeroMemory( void* ptr, size_t cnt )
{
   volatile char *vptr = (volatile char *)ptr;
   while (cnt) {
       *vptr = 0;
       vptr++;
       cnt--;
   }
}

并将其称为:

{
    char buffer[size];
    SecureZeroMemory( buffer, size );
}

然后由于buffer未被声明为volatile,因此使用指向volatile的指针并不重要 - 数据本身不是易失性的,因此写入变量不构成可观察行为(1.9 / 6)和允许编译器优化它们。

但是最近我发现一个声明,它只是重要的指针声明。具体来说,C ++ 03 5.3.1 / 1描述了这样的间接(*):

  

一元*运算符执行间接[...]如果表达式的类型是“指向T的指针”,则结果的类型为“T”。

所以声称因为在volatile char*上使用间接,我们得到volatile char并且写入那些确实构成了可观察的行为,并且不再重要的是如何声明实际数据。

C ++ 03 5.3.1 / 1间接描述是否真的保证使用上面示例中的volatile T*指针覆盖内存构成可观察行为并且不允许进行优化?

2 个答案:

答案 0 :(得分:4)

我很确定所有“新”引语都添加了*vptr是一个类型为volatile char的左值表达式。

左值的类型不会影响左值表达式引用的对象的类型,原因与指向非const对象的指针指针不会以某种方式形成对象的原因相同常量。因此原始分析不会影响此引用 - 对象仍然没有volatile限定类型。

按照通常的说法,我们会说*vptr的类型是volatile char &,但是5/5说,“如果表达式最初具有类型”引用T“,则类型将调整为在任何进一步分析之前的T“。这就是为什么*vptr被称为类型为volatile char而不是volatile char &的原因 - 在分析您从类型中删除引用的任何表达式之前,即使它是左值。

[编辑:我的回答曾经有一些关于cv资格的文本对于整数类型的非对象值是无关紧要的。这是真的(非类类型的左值到右值转换丢弃cv限定符,4.1 / 1)但不相关(我错误地认为,因为你引用的文本提到了一个非引用类型,它之后讨论的类型转化率)]

答案 1 :(得分:4)

这是一个有趣的问题。我认为的意图 标准是这应该工作。在阅读标准 (C ++ 03,§1.9/ 6,7)但是:

  

抽象机器的可观察行为是它的顺序   对易失性数据的读写和对库I / O的调用   功能

     

访问由volatile lvalue指定的对象,进行修改   一个对象,调用库I / O函数或调用   执行任何这些操作的功能都是一面的   效果,即执行状态的变化   环境。评价表达可能会产生副作用   效果。在执行序列中的某些指定点   称为序​​列点,以前的所有副作用   评估应完整,无副作用   随后的评估应该进行。

这两段中措辞的差异似乎是 显着:“可观察行为”是读取序列 并写入volatile 数据。在您的情况下,buffer 不是 易失性数据,因此编译器可以自由地进行优化 访问了。我不认为这是意图,但是 这似乎就是它所说的。

在您的情况下,优化会特别简单, 因为转换为volatile会在函数本身中发生。 编译器可以轻松确定vptr未指向 实际上是不稳定的数据。如果更改参数 键入void volatile*,然后编译器必须看到 呼叫站点和功能同时为了 安全地进行优化。

最后,不管标准是什么,编译器 对volatile有自己的解释。在实践中, 大多数(如果不是所有编译器)都会假设您正在使用 volatile有一个原因,并会生成机器 执行写操作的说明。 (在实践中,就是这样 他们会这样做,这意味着写入的顺序 在线程外部可见线程所在的代码 跑步仍未定义。这对您的使用不是问题, 但它有很多其他用途。)