Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/336.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# for循环的限制是一次计算还是与每个循环一起计算?_C#_Loops - Fatal编程技术网

C# for循环的限制是一次计算还是与每个循环一起计算?

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()

以下循环(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()
的值在循环过程中不会改变。

有两种方法可以解释您的问题:

  • 在每个循环上计算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!
}