循环条件评估

33

我有一个简短的问题。

我有一个循环,看起来像这样:

for (int i = 0; i < dim * dim; i++)

for循环中的条件在每次循环中都会重新评估吗?

如果是这样,像下面这样做是否更有效?:

int dimSquare = dim * dim;
for (int i = 0; i < dimSquare; i++)

谢谢

-Faken

3个回答

51

是的,语义上它将在每次循环中被评估。在某些情况下,编译器可能会自动从循环中删除条件,但并非总是如此。特别是:

void foo(const struct rect *r) {
  for (int i = 0; i < r->width * r->height; i++) {
    quux();
  }
}
编译器不能在这种情况下移动乘法操作,因为对于它而言,quux() 函数修改了 r 的值,所以局部变量通常才能被提取出循环表达式(假设你不会取地址)。虽然在某些条件下结构体成员也可能被提取,但许多情况下编译器会认为内存中的内容已经发生改变,比如写入任何指针或调用几乎任何函数。因此,如果你在循环中使用了任何非局部变量,最好假设优化不会发生。
总之,通常只建议在以下情况下主动将可能昂贵的代码从条件语句中提取出来:
  • 不影响可读性
  • 明显需要花费很长时间(例如网络访问)
  • 或者在分析性能瓶颈时该代码是热点。

7
请注意,在您的示例中,r可以是const struct rect *,但编译器仍然不允许假定quux()不会通过别名修改其数据成员。通常认为const会激活各种编译器优化,但实际上并不像人们最初想象的那么简单。 - Steve Jessop
@onebyone:确实。不过,Restrict 可能能在这方面提供帮助。 - bdonlan
是的,对于既是C99编译器又支持restrict的C++编译器。而且严格的别名规则有助于“写入几乎任何指针”,但不能用于“调用几乎任何函数”。内联应该有助于函数。 - Steve Jessop
quux() 不可能修改 r,除非 r 是一个全局变量。我有什么遗漏吗? - GManNickG
3
是的,'r' 很可能指向一个全局变量,或者是由全局变量指向的内容,等等。 - bdonlan
1
或者是一个具有非全局名称的“全局变量”,例如静态类成员。甚至是一个函数静态变量。 - MSalters

38

一般来说,如果您在循环内更改“dim”的值,它将每次重新评估。但由于在您的示例中不是这种情况,一个好的编译器会优化您的代码,您不会看到性能上的任何差异。


抱歉对于那位思路非常详细的回答者,但是这个回答以一种清晰简洁的方式向我解释了一切。 - Faken
3
不要忘记牢记bdonlan有关使用本地变量进行循环控制的建议 - 由于本地变量的可见性受限,所以编译器更容易进行优化(这也有助于阅读者理解,不仅是编译器优化)。 - Michael Burr
如果我是你,我不会道歉。这样可以让bdonlan有机会获得“大众化”徽章,在此撰写时还需要9票(如果Lulu再得到8票即可)。 - Steve Jessop
@onebyone,好吧,如果你这么说,我想我也得点赞了;) - bdonlan
2
一个好的编译器也希望能够将你的i++变成++i :-) - David Claridge
@David,我必须在$work中使用一个相当糟糕的编译器,但至少它能够正确地完成这个任务 :) - bdonlan

-4

编译器将在循环开始之前预先计算Dim * Dim的值。


除非Dim可以在循环内部修改。 - Mike Daniels
那么你的意思是,在 for 循环中,即使 dim 的值在循环期间发生变化,循环也会一直运行直到预编译的值被达到?还是当 dim 在循环中途改变时,程序将重新评估它? - Faken
1
@Faken,如果Dim有任何可能改变的可能性,编译器将在每次循环中重新评估它。 - bdonlan
但在这个例子中,因为问题被问出,它不能。所以它将在循环之前预先计算一次。 - Charles Bretana

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接