6

伙计们,

考虑一下这段(可恶的)代码:

volatile unsigned long a[1];  
unsigned long T; 

void main(void) 
{    
    a[0] = 0x6675636b;   /* first access of a */
    T = *a; 
    *(((char *)a) + 3) = 0x64; /* second access of a */
    T = *a;
}

...问题:是((char *)a)易失性还是​​非易失性?

这就引出了一个更大的问题:a 的两个访问之间是否存在依赖关系?也就是说,人类常识说有,但是 C99 标准说 volatile 的东西不会给 non-volatile 的东西加上别名——所以如果((char *)a)是 non-volatile,那么这两个访问不会别名,并且没有依赖。

更准确地说,C99 6.7.3(第 5 段)如下:

“如果尝试通过使用具有非 volatile 限定类型的左值来引用具有 volatile 限定类型定义的对象,则行为未定义。”

那么当我们进行类型转换时a,是否使用 volatile 限定符?

4

2 回答 2

1

如有疑问,请运行一些代码 :) 我编写了一些类似(稍微不那么可恶)的测试代码(msvs 2k10 中的 win32 C++ 应用程序)

int _tmain(int argc, _TCHAR* argv[]) {
    int a = 0;
    volatile int b = 0;

    a = 1; //breakpoint 1
    b = 2; //breakpoint 2
    *(int *) &b  = 0; //breakpoint 3
    *(volatile int *) &b  = 0; //breakpoint 4

    return 0;
}

在为发布而编译时,我可以在 2 和 4 处断点,但不能在 1 和 3 处。

我的结论是类型转换决定了行为,并且 1 和 3 被优化掉了。直觉支持这一点 - 否则编译器必须保留某种类型的所有内存位置列表作为易失性并检查每次访问(硬,丑陋),而不是仅仅将它与标识符的类型相关联(更容易和更直观) .

我还怀疑它是特定于编译器的(甚至在编译器中也可能是特定于标志的),并且会在取决于此行为之前在任何平台上进行测试。

实际上从头开始,我只是尽量不依赖这种行为:)

另外,我知道您专门询问数组,但我怀疑这会有所不同。您可以轻松地为数组编写类似的测试代码。

于 2012-11-07T17:55:41.433 回答
0

就像你说的,它的“未定义”。这意味着恶魔可以从你的鼻子里出来。请尽可能坚持“定义”的行为。说明volatile符会要求编译器不要优化该值,因为它是一个“重要”和临界值,如果由于不同的优化机制而发生更改,可能会导致问题。但这就是它所能做的。

于 2012-11-02T16:42:33.280 回答