for循环的限制是计算一次还是每次循环?

soo*_*ise 19 c# loops

以下循环(12332*324234)中的限制是计算一次还是每次循环运行?

for(int i=0; i<12332*324234;i++)
{
    //Do something!
}
Run Code Online (Sandbox Code Playgroud)

KLe*_*ee1 31

为此它计算一次,或更可能是0次.

编译器将为您优化乘法.

然而,如果你有类似的东西,情况并非总是如此.

for(int i=0; i<someFunction();i++)
{
    //Do something!
}
Run Code Online (Sandbox Code Playgroud)

因为编译器并不总是能够看到someFunction将返回的内容.因此,即使someFunction每次都返回一个常量值,如果编译器不知道,它也无法对其进行优化.

编辑:正如MainMa在评论中所说,在这种情况下,您可以通过执行以下操作来消除成本:

int limit = someFunction();
for(int i=0; i<limit ;i++)
{
    //Do something!
}
Run Code Online (Sandbox Code Playgroud)

如果您确定someFunction()在循环期间值不会改变.


LBu*_*kin 14

这是C#中最常被误解的循环行为之一.

这是你需要知道的:

循环边界计算,如果非常量并涉及变量,属性访问,函数调用或委托调用,则会在循环的每次迭代之前重新计算边界的值.

所以,例如:

for( int i = 0; i < 1234*1234; i++ ) { ... }
Run Code Online (Sandbox Code Playgroud)

在这种情况下,表达式1234*1234是编译时常量,因此在每次迭代时都不会重新计算.实际上,它是在编译时计算的,并用常量替换.

但是,在这种情况下:

int k = 10;
for( int i = 0; i < k; i++ ) { k -= 1; ... }
Run Code Online (Sandbox Code Playgroud)

k必须在每次迭代时检查值.毕竟它可以改变 ..在这个例子中.幸运的是,由于k它只是一个局部变量,访问它的成本非常低 - 在许多情况下它将保留在本地CPU缓存中,甚至可能保存在寄存器中(取决于JIT如何处理和发出机器)码).

如果出现以下情况:

IEnumerable<int> sequence = ...;
for( int i = 0; i < sequence.Count(); i++ ) { ... }
Run Code Online (Sandbox Code Playgroud)

计算的成本sequence.Count()可能非常昂贵.并且由于它在循环的每次迭代中进行评估,因此可以快速累加.

编译器无法优化对循环边界表达式中发生的方法或属性的调用,因为它们也可能随每次迭代而更改.想象一下,如果上面的循环写成:

IEnumerable<int> sequence = ...;
for( int i = 0; i < sequence.Count(); i++ ) {
    sequence = sequence.Concat( anotherItem );
}
Run Code Online (Sandbox Code Playgroud)

显然sequence每次迭代都在改变......因此每次迭代Count()可能会有所不同.编译器不会尝试执行某些静态分析来确定循环边界表达式是否可以是常量...即使不是不可能,也会非常复杂.相反,它假设如果表达式不是常量,则必须在每次迭代时对其进行求值.

现在,在大多数情况下,计算循环边界约束的成本相对便宜,因此您不必担心它.但是你需要了解编译器如何处理这样的循环边界.此外,作为开发人员,您需要注意使用具有副作用的属性或方法作为边界表达式的一部分 - 毕竟,这些副作用将在循环的每次迭代中发生.


Cha*_*ion 7

实际上,这将无法编译,因为它会溢出但如果你把它变成一个较小的数字并打开Reflector你会发现这样的东西.

for (int i = 0; i < 0x3cf7b0; i++)
{

}
Run Code Online (Sandbox Code Playgroud)