6

考虑 ANSI C 中的一个 while 循环,其唯一目的是延迟执行:

unsigned long counter = DELAY_COUNT;
while(counter--);

我已经看到这在嵌入式系统上被大量使用来强制延迟,例如。没有sleep功能,定时器或中断是有限的。

我对 ANSI C 标准的解读是,这可以通过符合标准的编译器完全删除。它没有5.1.2.3中描述的副作用:

访问 volatile 对象、修改对象、修改文件或调用执行任何这些操作的函数都是副作用,它们是执行环境状态的变化。

...这部分还说:

如果一个实际的实现可以推断出它的值没有被使用并且没有产生所需的副作用(包括调用函数或访问易失性对象引起的任何副作用),则它不需要评估表达式的一部分。

这是否意味着可以优化循环?即使countervolatile

笔记:

  1. 这与允许编译器消除无限循环不完全相同吗?,因为这指的是无限循环,并且关于何时允许程序终止的问题出现了。在这种情况下,程序肯定会在某个时候越过这条线,无论是否优化。
  2. 我知道 GCC 做了什么(删除循环 for-O1或更高,除非counteris volatile),但我想知道标准规定了什么。
4

4 回答 4

12

C 标准合规性遵循“as-if”规则,编译器可以根据该规则生成任何行为“好像”它在抽象机器上运行您的实际指令的代码。由于不执行任何操作具有与执行循环“好像”相同的可观察行为,因此完全允许不为其生成代码。

换句话说,在真实机器上进行计算所花费的时间不是程序“可观察”行为的一部分,它只是特定实现的一种现象。

变量的情况有所不同volatile,因为访问 volatile 算作“可观察”的效果。

于 2013-01-21T23:35:36.243 回答
9

这是否意味着可以优化循环?

是的。

即使计数器不稳定?

不,它会读取和写入具有可观察行为的 volatile 变量,因此它必须发生。

于 2013-01-21T23:34:49.910 回答
2

如果计数器为volatile,则编译器无法合法地优化延迟循环。否则可以。

像这样的延迟循环很糟糕,因为它们燃烧的时间取决于编译器如何为它们生成代码。使用不同的优化选项,您可以实现不同的延迟,这几乎不是延迟循环想要的。

出于这个原因,这种延迟循环应该用汇编语言实现,程序员可以完全控制代码。这通常适用于具有简单 CPU 的嵌入式系统。

于 2013-01-21T23:35:31.203 回答
1

标准规定了您所看到的行为。如果您为您创建一个依赖关系树,DELAY_COUNT您会看到它有一个 modify without use 属性,这意味着它可以被消除。这是参考非易失性案例。在 volatile 情况下,编译器无法使用依赖树来尝试删除此变量,因此延迟仍然存在(因为 volatile 意味着硬件可以更改内存映射值,或者在某些情况下意味着“我真的需要这个不要扔它away”)如果您正在查看是否标记为 volatile,它会告诉编译器,请不要把它扔掉它在这里是有原因的。

于 2013-01-21T23:36:44.447 回答