13

这基本上是这个问题的延续。到目前为止,如果我有这样的功能,它看起来像这样:

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 的指针并不重要 - 数据本身不是 volatile,因此写入变量不构成可观察的行为(1.9/6),并且允许编译器优化它们.

但是最近我遇到了一个声明,即只有指针声明才重要。特别是 C++03 5.3.1/1 描述了这样的间接(*):

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

因此,声称是因为在volatile char*我们获取volatile char和写入的 a 上使用了间接性,它们确实构成了可观察的行为,并且如何声明实际数据不再重要。

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

4

2 回答 2

4

我很确定所有“新”引用添加的*vptr是一个带有 type 的左值表达式volatile char

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

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

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

于 2012-12-06T14:51:04.963 回答
4

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

抽象机的可观察行为是它对易失性数据的读取和写入顺序以及对库 I/O 函数的调用。

访问由 volatile 左值指定的对象、修改对象、调用库 I/O 函数或调用执行任何这些操作的函数都是副作用,它们是执行环境状态的变化。表达式的评估可能会产生副作用。在被称为序列点的执行序列中的某些指定点处,先前评估的所有副作用都应是完整的,并且后续评估的副作用不应发生。

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

在您的情况下,优化将特别简单,因为转换为 volatile 发生在函数本身中。编译器可以很容易地确定它vptr不指向实际易失的数据。如果将参数类型更改为void volatile*,则编译器必须同时查看调用站点和函数才能安全地进行优化。

最后,不管标准怎么说,编译器对volatile. 在实践中,如果不是所有编译器,大多数编译器都会假设您使用 volatile是有原因的,并且会生成机器指令来执行写入操作。(实际上,这就是他们要做的所有事情,这意味着写入在线程之外变得可见的顺序,线程正在运行的代码仍未定义。这对您的使用来说不是问题,但它是用于许多其他用途。)

于 2012-12-06T14:58:23.920 回答