C# for循环的限制是一次计算还是与每个循环一起计算?
以下循环(12332*324234)中的限制是一次计算还是每次循环运行时计算C# for循环的限制是一次计算还是与每个循环一起计算?,c#,loops,C#,Loops,以下循环(12332*324234)中的限制是一次计算还是每次循环运行时计算 for(int i=0; i<12332*324234;i++) { //Do something! } for(inti=0;i对于这个,它计算了一次,或者更可能是0次 编译器将为您优化乘法运算 然而,如果你有类似的东西,情况并不总是如此 for(int i=0; i<someFunction();i++) { //Do something! } 如果您确信someFunction()
for(int i=0; i<12332*324234;i++)
{
//Do something!
}
for(inti=0;i对于这个,它计算了一次,或者更可能是0次
编译器将为您优化乘法运算
然而,如果你有类似的东西,情况并不总是如此
for(int i=0; i<someFunction();i++)
{
//Do something!
}
如果您确信someFunction()
的值在循环过程中不会改变。有两种方法可以解释您的问题:
- 在每个循环上计算12332*324234的乘法
- 是否对每个循环计算循环条件中的表达式
这两个不同问题的答案是:
- 不,它实际上是在编译时计算的,因为它包含两个常量
- 是的,如果有必要的话,是的
换言之:
for (int i = 0; i < someString.Length; i++)
for(int i=0;i
如果对someString.Length
的求值代价高昂,那么每次循环迭代都会受到惩罚。实际上,这不会编译,因为它会溢出,但如果将其设置为较小的数字并打开Reflector,您会发现类似的情况
for (int i = 0; i < 0x3cf7b0; i++)
{
}
for(int i=0;i<0x3cf7b0;i++)
{
}
正如@Chaos所指出的,它不会编译。但是如果使用可表示的表达式(如100*100),结果可能会被硬编码。在Mono上,CIL包括:
IL_0007: ldloc.0
IL_0008: ldc.i4.1
IL_0009: add
IL_000a: stloc.0
IL_000b: ldloc.0
IL_000c: ldc.i4 10000
IL_0011: blt IL_0007
如您所见,100*100硬编码为10000。但是,通常每次都会对其进行计算,如果调用方法或属性,则可能无法对其进行优化。它看起来每次都会进行计算。从VS2008进行反汇编
0000003b nop
for (Int64 i = 0; i < (Int64)12332 * (Int64)324234; i++)
0000003c mov qword ptr [rsp+20h],0
00000045 jmp 000000000000005E
{
00000047 nop
bool h = false;
00000048 mov byte ptr [rsp+28h],0
}
0000004d nop
for (Int64 i = 0; i < (Int64)12332 * (Int64)324234; i++)
0000004e mov rax,qword ptr [rsp+20h]
00000053 add rax,1
00000059 mov qword ptr [rsp+20h],rax
0000005e xor ecx,ecx
00000060 mov eax,0EE538FB8h
00000065 cmp qword ptr [rsp+20h],rax
0000006a setl cl
0000006d mov dword ptr [rsp+2Ch],ecx
00000071 movzx eax,byte ptr [rsp+2Ch]
00000076 mov byte ptr [rsp+29h],al
0000007a movzx eax,byte ptr [rsp+29h]
0000007f test eax,eax
00000081 jne 0000000000000047
0000003b nop
对于(Int64 i=0;i<(Int64)12332*(Int64)324234;i++)
0000003c移动qword ptr[rsp+20h],0
000000 45 jmp 00000000000000 5E
{
00000047无
bool h=假;
000000 48 mov字节ptr[rsp+28h],0
}
000000 4D无
对于(Int64 i=0;i<(Int64)12332*(Int64)324234;i++)
0000004e移动速率,qword ptr[rsp+20h]
00000053加上rax,1
000000 59 mov qword ptr[rsp+20小时],rax
0000005e异或ecx,ecx
000000 60 mov eax,0EE538FB8h
000000 65 cmp qword ptr[rsp+20小时],rax
000000 6A setl cl
000000 6d移动dword ptr[rsp+2Ch],ecx
000000 71 movzx eax,字节ptr[rsp+2Ch]
000000 76 mov字节ptr[rsp+29h],al
0000007a movzx eax,字节ptr[rsp+29h]
000000 7F测试eax,eax
00000081 jne 00000000000000 47
这是C#中循环最常被误解的行为之一
以下是您需要知道的:
循环边界计算,如果
非常量且包含一个变量,
属性访问、函数调用或委托调用
将重新计算每个
循环的迭代
例如:
for( int i = 0; i < 1234*1234; i++ ) { ... }
k
的值必须在每次迭代时检查。毕竟它可以更改。在本例中是可以更改的。幸运的是,由于k
只是一个局部变量,访问它的成本非常低-在许多情况下,它要么保留在本地CPU缓存中,要么甚至保存在寄存器中(取决于JIT处理和发出机器代码的方式)
在以下情况下:
IEnumerable<int> sequence = ...;
for( int i = 0; i < sequence.Count(); i++ ) { ... }
显然,序列
在每次迭代中都在变化……因此,计数()
在每次迭代中可能会有所不同。编译器不会尝试执行一些静态分析来确定循环边界表达式是否可以是常量…这将非常复杂,如果不是不可能的话。相反,它假设如果表达式不是常量,则必须在每次迭代中对其求值
现在,在大多数情况下,计算循环边界约束的成本相对较低,因此您不必担心。但您确实需要了解编译器是如何这样对待循环边界的。此外,作为开发人员您需要小心使用具有副作用的属性或方法f边界表达式-毕竟,这些副作用会在循环的每次迭代中发生。是的,比较值是在每个循环周期计算的
如果您必须使用for循环,这几乎不再需要了,那么这里只有一个“好”的循环模板:
for (int i = first(), last = last(); i != last; ++i)
{
// body
}
请注意前缀的增量。首先,问题中的for循环不会编译。但假设它是
for (int i = 0; i < 20; i++)
{
Console.WriteLine(i);
i++;
}
现在即使我用循环中的函数替换它
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < Foo(); i++)
{
Console.WriteLine(i);
i++;
}
Console.Read();
}
private static int Foo()
{
return 20;
}
在我看来是一样的
所以在我看来,对于有限极限的FOR循环,对于计算极限的FOR循环,对于来自函数的极限的FOR循环,没有区别
因此,只要代码能够复制,您就知道在这样一个庞大的循环中您在做什么,并且在这个过程中您有足够的内存,我认为它会工作并产生相同的性能。(在C#)中)除了KLee1的答案之外,我正在编写完全相同的东西,只是稍微不同,以使它更具可读性:
for(int i=0, limit = someFunction(); i<limit ;i++)
{
//Do something!
}
for(int i=0,limit=someFunction();我可能是@Darin的副本,不是真的。它使用了一个方法/属性,而不是常量。这是个糟糕的例子;编译器在任何地方都会像这样优化静态计算。另一方面,VB与C#相反。使用VB时,循环限制总是计算一次。True。例如,在someFunction()中对数据库进行查询
这真的是个坏主意。你可以在循环之前创建一个整数,给它赋值,然后在循环中使用这个整数,这样做很容易避免。这是错误的,原因有很多。这不是一个“必要”的问题。因为循环从来都不是必要的,但有时也会有
for (int i = 0; i < 20; i++)
{
Console.WriteLine(i);
i++;
}
for (int i = 0; i < 10*2; i++)
{
Console.WriteLine(i);
i++;
}
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] int32 i,
[1] bool CS$4$0000)
L_0000: nop
L_0001: ldc.i4.0
L_0002: stloc.0
L_0003: br.s L_0016
L_0005: nop
L_0006: ldloc.0
L_0007: call void [mscorlib]System.Console::WriteLine(int32)
L_000c: nop
L_000d: ldloc.0
L_000e: ldc.i4.1
L_000f: add
L_0010: stloc.0
L_0011: nop
L_0012: ldloc.0
L_0013: ldc.i4.1
L_0014: add
L_0015: stloc.0
L_0016: ldloc.0
L_0017: ldc.i4.s 20
L_0019: clt
L_001b: stloc.1
L_001c: ldloc.1
L_001d: brtrue.s L_0005
L_001f: call int32 [mscorlib]System.Console::Read()
L_0024: pop
L_0025: ret
}
class Program
{
static void Main(string[] args)
{
for (int i = 0; i < Foo(); i++)
{
Console.WriteLine(i);
i++;
}
Console.Read();
}
private static int Foo()
{
return 20;
}
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 2
.locals init (
[0] int32 i,
[1] bool CS$4$0000)
L_0000: nop
L_0001: ldc.i4.0
L_0002: stloc.0
L_0003: br.s L_0016
L_0005: nop
L_0006: ldloc.0
L_0007: call void [mscorlib]System.Console::WriteLine(int32)
L_000c: nop
L_000d: ldloc.0
L_000e: ldc.i4.1
L_000f: add
L_0010: stloc.0
L_0011: nop
L_0012: ldloc.0
L_0013: ldc.i4.1
L_0014: add
L_0015: stloc.0
L_0016: ldloc.0
L_0017: call int32 TestBedForums.Program::Foo()
L_001c: clt
L_001e: stloc.1
L_001f: ldloc.1
L_0020: brtrue.s L_0005
L_0022: call int32 [mscorlib]System.Console::Read()
L_0027: pop
L_0028: ret
}
for(int i=0, limit = someFunction(); i<limit ;i++)
{
//Do something!
}