我不太了解5.1.2.3/3的以下部分:
如果表达式可以推导出它,那么实际的实现不需要评估表达式的一部分 不使用值,不产生任何副作用(包括由...引起的任何副作用) 调用函数或访问volatile对象。)
假设我有以下代码:
char data[size];
int i;
int found;
/* initialize data to some values in here */
found = 0;
for( i = 0; i < size; i++ ) {
if( data[i] == 0 ) {
found = 1;
/* no break in here */
}
}
/* i no longer used, do something with "found" here */
请注意,found
以0
开头,可以保持不变或变为1
。它不能变成1
然后变成别的东西。因此,以下代码将产生相同的结果(除了i
值之外,无论如何都不会在循环之后使用):
char data[size];
int i;
int found;
/* initialize data to some values in here */
found = 0;
for( i = 0; i < size; i++ ) {
if( data[i] == 0 ) {
found = 1;
break;
}
}
/* i no longer used, do something with "found" here */
现在标准所说的关于的内容不需要评估关于found = 1
的表达式的部分内容以及循环控制表达式,它们在第一次迭代之后进行控制if
显然,如果在此代码之后的某处使用found
,编译器必须发出遍历数组的代码并有条件地评估found = 1
表达式。
对于数组中找到的每个零,是否需要对found = 1
进行一次评估,或者它是否可以将其评估为一次,因此在编译第一个代码段时会有效地为第二个代码段发出代码?
答案 0 :(得分:6)
它可以改为再一次评估它,因此在编译第一个片段时有效地为第二个片段发出代码吗?
是的,编译器有权执行该优化。这似乎是一个非常积极的优化,但它是合法的。
看一个更符合文本精神的例子可能会很有趣:
实际实现不需要评估表达式的一部分,如果它可以推断出它的值没有被使用并且没有产生所需的副作用(包括由调用函数或访问volatile对象引起的任何副作用)。
假设我们有:
int x = pureFunction(y) * otherPureFunction(z);
假设编译器知道两个函数都是int-returns&#34; pure&#34;功能;也就是说,它们没有副作用,它们的结果完全取决于论点。假设编译器还认为otherPureFunction
是一个非常昂贵的操作。编译器可以像您编写的那样选择实现代码:
int temp = pureFunction(y);
int x = temp == 0 ? 0 : temp * otherPureFunction(z);
即,确定在某些条件下不必计算otherPureFunction()
,因为一旦知道左操作数为零,乘法结果就已知。没有必要的副作用将被省略,因为没有副作用。
答案 1 :(得分:5)
是的,它可以执行此优化,因为没有I / O操作,从volatile
位置读取或优化代码省略的外部可见写入内存,因此保留了行为。
作为这种优化的一个例子,GCC将编译
void noop(const char *s)
{
for (size_t i = 0; i < strlen(s); i++) {
}
}
到一个完全空的函数:
noop:
.LFB33:
.cfi_startproc
rep ret
.cfi_endproc
允许这样做,因为标准保证strlen
的行为,编译器知道它对s
或任何其他内存没有外部可见影响,并且可以推断出整个功能没有任何行为。 (令人惊讶的是,这种简单的优化将复杂性从二次变为常数。)