1

The C Standard says

An actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no needed side effects are produced (including any caused by calling a function or accessing a volatile object)

When is a volatile variable not needed? According to this paragraph, volatile appears to become subject to the as-if rule just like any other non-volatile object.


The answers given in the non-duplicate linked question are not helpful to me as they do not address the above quoted paragraph

  • When is a value considered to be "used"? It appears to be different than "reading the value from an object" because the corresponding access can be omitted, according to the above quote.
  • What is a "needed side effect"?

Refer to the comments below.

4

5 回答 5

1

我认为他们可能指的是这样的情况

void test(int volatile *y)
{
    int x = *y;
}

int main(void)
{
    test(some_volatile_y);
    return 0;
}

内存中的负载可以被优化掉(未评估),因为它的结果不一定在每个实现中都需要。(当然,在内存加载可以触发某些事件的实现中,结果当然是“需要的”并且不能被优化掉。)


感谢 Alex 在下面的评论中指出了 C 标准中的这个脚注(C99:6.7.3 类型限定符:脚注 114):

volatile 声明可用于描述对应于内存映射输入/输出端口的对象或由异步中断函数访问的对象。对如此声明的对象的操作不应被实现“优化”或重新排序,除非评估表达式的规则允许。

请注意,它说可能——它是在举例说明。A 子句 J.3 然后提到对具有 volatile 限定类型的对象的访问是实现定义的,这是有道理的:前面的子句是“访问”可能意味着什么的示例,但它可能意味着什么其他取决于实现。

因此,在系统知道这不会导致任何需要的副作用的实现中,可以删除易失性访问。

于 2013-04-20T09:51:00.843 回答
1

这部分标准意图是说可以删除以下代码中的 volatile 读取,因为它永远不会被评估:

volatile a;
if (0 && a) something();

但不是下一个中的易失性读取

volatile a;
int b;
b = 0*a;

作为围绕 mehrdad 回答的讨论的后续行动:我确实认为 J.3.10

J.3 实现定义的行为

J.3.10 限定词

— 什么构成对具有 volatile 限定类型的对象的访问(6.7.3)。

意味着编译器可以定义无意义的volatile访问,从而允许自己基本上忽略它。当然它也不能实现任何信号,但是可以忽略异步信号的存在。唯一不能忽视的地方volatile就是a周围longjmp

当然,这样的选择使得这个编译器对于任何严肃的系统开发都毫无用处,但是一些应用程序不需要它,并且该标准允许对这种简单的体系结构及其应用程序进行兼容的实现。

于 2013-04-20T09:44:44.417 回答
0

我认为这里的关键点是“评估表达式的一部分”。所以,如果我们有:

volatile int *wd_ptr = (volatile int *)0xF9000000;   // My watchdog timer. 

#define FREQUENCY_MULTIPLIER (4.9171E6/1000)

void wd_tickle()
{
    int time_left;

    time_left = (int)*wd_ptr * FREQUENCY_MULTIPLIER; 
    // Side effect of reading the , resets the timer to it's programmed value.
#if WD_DEBUG
    if (time_left < 100000)
    {
        printf("Oops, less than 100000 ticks left on watchdog.\n"
               "Maybe you need to tickle the watchdog a bit more often\n");
    }
#endif
}

现在,编译器可以消除我们从计算 time-left 中得到的浮点乘法,但它不能删除对*wd_ptr.

于 2013-04-20T10:27:04.800 回答
0

什么时候不需要 volatile 变量?

在我看来,您可能误解了 5.1.2.3p4。如果编译器可以证明以下两个,那么它可以优化一个表达式:

  1. 不使用它的值。
  2. 不会产生任何需要的副作用(包括由调用函数或访问 volatile 对象引起的任何副作用)。

总之,编译器无法优化访问 volatile 对象的表达式。


编译器现在是否决定我是否需要对 volatile 访问产生副作用?

标准决定了这一点,而编译器通过成为符合标准的实现来决定。5.1.2.3p6:对 volatile 对象的访问严格按照抽象机的规则进行评估。

于 2013-04-20T10:44:01.283 回答
-1

如果我(现在)正确理解了这个问题,那么您会问为什么标准说“需要副作用”,以及这是否意味着兼容的编译器可以省略它可以证明在某种程度上不是“需要”的访问。

我的简单解读是他们使用“需要的副作用”来表示总是需要副作用,而不是作为修饰符,并且他们可以简单地从上面的段落中删除“需要”而不改变含义。

(但是,我没有标准的副本来确认它是否确定副作用是否可以确定为“需要”。似乎不太可能。)

于 2013-04-20T10:29:32.397 回答