GOTO和DO的区别
本文关键字:区别 DO GOTO | 更新日期: 2023-09-27 18:04:57
以下两个c#代码段在执行上有区别吗?
do
{
Console.WriteLine(x.ToString());
++x;
}
while (x < 7);
和
label:
{
Console.WriteLine(x.ToString());
++x;
}
if (x < 7) goto label;
我想弄明白为什么情况如此糟糕。谢谢。
编辑:如果我添加括号,代码段非常相似。
EDIT2:在Visual Studio中,我点击了Go to Disassembly,我得到了以下代码:
do
{
00000037 nop
Console.WriteLine(x.ToString());
00000038 lea ecx,[ebp-40h]
0000003b call 63129C98
00000040 mov dword ptr [ebp-48h],eax
00000043 mov ecx,dword ptr [ebp-48h]
00000046 call 63148168
0000004b nop
++x;
0000004c inc dword ptr [ebp-40h]
}
0000004f nop
while (x < 7);
00000050 cmp dword ptr [ebp-40h],7
00000054 setl al
00000057 movzx eax,al
0000005a mov dword ptr [ebp-44h],eax
0000005d cmp dword ptr [ebp-44h],0
00000061 jne 00000037
和
label:
{
Console.WriteLine(x.ToString());
00000069 lea ecx,[ebp-40h]
0000006c call 63129C98
00000071 mov dword ptr [ebp-4Ch],eax
00000074 mov ecx,dword ptr [ebp-4Ch]
00000077 call 63148168
0000007c nop
++x;
0000007d inc dword ptr [ebp-40h]
}
00000080 nop
if (x < 7) goto label;
00000081 cmp dword ptr [ebp-40h],7
00000085 setge al
00000088 movzx eax,al
0000008b mov dword ptr [ebp-44h],eax
0000008e cmp dword ptr [ebp-44h],0
00000092 jne 00000097
00000094 nop
00000095 jmp 00000068
区别是无条件跳转
不,我甚至认为while
在后面是这样实现的。
使用goto
的缺点是它鼓励在代码中来回切换(也被称为"意大利面条代码":它是一团乱)。这会让你的代码难以阅读、调试和分析,还会带来bug,因为你无法真正理解到底发生了什么。
while
的好处是,你可以理解它,编译器可以理解它,所以它可以给你很好的警告。
让我们看一下IL:
.method private hidebysig
instance void While () cil managed
{
// Method begins at RVA 0x2050
// Code size 31 (0x1f)
.maxstack 2
.locals init (
[0] int32 x,
[1] bool CS$4$0000
)
IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: stloc.0
// loop start (head: IL_0003)
IL_0003: nop
IL_0004: ldloca.s x
IL_0006: call instance string [mscorlib]System.Int32::ToString()
IL_000b: call void [mscorlib]System.Console::WriteLine(string)
IL_0010: nop
IL_0011: ldloc.0
IL_0012: ldc.i4.1
IL_0013: add
IL_0014: stloc.0
IL_0015: nop
IL_0016: ldloc.0
IL_0017: ldc.i4.7
IL_0018: clt
IL_001a: stloc.1
IL_001b: ldloc.1
IL_001c: brtrue.s IL_0003
// end loop
IL_001e: ret
} // end of method Program::While
.method private hidebysig
instance void Goto () cil managed
{
// Method begins at RVA 0x207c
// Code size 34 (0x22)
.maxstack 2
.locals init (
[0] int32 x,
[1] bool CS$4$0000
)
IL_0000: nop
IL_0001: ldc.i4.0
IL_0002: stloc.0
// loop start (head: IL_0003)
IL_0003: ldloca.s x
IL_0005: call instance string [mscorlib]System.Int32::ToString()
IL_000a: call void [mscorlib]System.Console::WriteLine(string)
IL_000f: nop
IL_0010: ldloc.0
IL_0011: ldc.i4.1
IL_0012: add
IL_0013: stloc.0
IL_0014: ldloc.0
IL_0015: ldc.i4.7
IL_0016: clt
IL_0018: ldc.i4.0
IL_0019: ceq
IL_001b: stloc.1
IL_001c: ldloc.1
IL_001d: brtrue.s IL_0021
IL_001f: br.s IL_0003
// end loop
IL_0021: ret
} // end of method Program::Goto
ILSpy事件将goto标记为循环。当将其反编译为c#时,它甚至将两个循环显示为do while
。
但是有不同的!while循环的作用域是:
int x = 0;
do
{
string z = "TEST";
Console.WriteLine(x.ToString());
++x;
}
while (x < 7);
Console.WriteLine(z); // invalid!
但这是有效的:
int x = 0;
label:
string z = "TEST";
Console.WriteLine(x.ToString());
++x;
if (x < 7) goto label;
Console.WriteLine(z);
你应该使用它吗?不!