Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.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# 添加一个或多个int变量时生成的不同IL_C#_Il_Csc_Ildasm - Fatal编程技术网

C# 添加一个或多个int变量时生成的不同IL

C# 添加一个或多个int变量时生成的不同IL,c#,il,csc,ildasm,C#,Il,Csc,Ildasm,我有一个c语言的程序: 当我编译它时,下面是编译器为Main生成的IL: .method public hidebysig static void Main() cil managed { .entrypoint // Code size 30 (0x1e) .maxstack 1 .locals init (int32 V_0, float64 V_1, float64 V_2, string V

我有一个c语言的程序:

当我编译它时,下面是编译器为Main生成的IL:

.method public hidebysig static void  Main() cil managed
{
  .entrypoint
  // Code size       30 (0x1e)
  .maxstack  1
  .locals init (int32 V_0,
           float64 V_1,
           float64 V_2,
           string V_3)
  IL_0000:  nop
  IL_0001:  ldc.i4.4
  IL_0002:  stloc.0
  IL_0003:  ldc.r8     12.34
  IL_000c:  stloc.1
  IL_000d:  ldc.r8     3.1415926535897931
  IL_0016:  stloc.2
  IL_0017:  ldstr      "Ehsan"
  IL_001c:  stloc.3
  IL_001d:  ret
} // end of method Program::Main
这很好,我理解,现在如果我添加另一个整数变量,那么会生成不同的结果,下面是修改后的c代码:

下面是根据上述c代码生成的IL:

如果您现在注意到
stloc.s
语句是用
V_4
生成的,这是本地的,但我不清楚这一点,我也不知道这些本地的目的是什么,我的意思是:

 .locals init (int32 V_0,
               float64 V_1,
               float64 V_2,
               string V_3)
有些事情需要注意

首先,这可能是一个调试构建,或者至少在编译过程中关闭了某些优化。我希望在这里看到的是:

.method public hidebysig static void Main () cil managed 
{
  .entrypoint

  IL_0000: ret
}
也就是说,由于没有使用这些局部变量,我希望编译器完全跳过它们。它不会出现在调试版本上,但这是一个很好的例子,说明了C#和IL所说的内容之间可能存在巨大差异

接下来要注意的是IL方法是如何构造的。您有一个本地值数组,该数组由各种类型的
.locals
块定义。这些通常与C#所拥有的相当接近,尽管通常会有捷径和重新安排

最后,我们有一组指令,所有指令都作用于这些局部变量、任何参数和一个堆栈,它可以推送到该堆栈,从该堆栈中弹出,并且各种指令将在该堆栈上交互

接下来要注意的是,您在这里看到的IL是一种字节码的汇编:这里的每个指令都有一对一映射到一个或两个字节,每个值也消耗一定数量的字节。因此,例如,
stloc V_4
(在您的示例中没有实际出现,但我们会得出结论)将映射到
0xFE 0x0E 0x04 0x00
,其中
0xFE 0x0E
stloc
的编码,而
0x04 0x00
是所讨论的本地索引。它的意思是“弹出堆栈顶部的值,并将其存储在第5个(索引4)本地”

现在,这里有一些缩写。其中之一是多条指令的
.s
“简短”形式(
\u s
以等效的
系统.Reflection.Emit.OpCode
值的名义)。这些是采用单字节值(有符号或无符号,取决于指令)的其他指令的变体,而另一种形式采用两字节或四字节值,通常是索引或相对跳转距离。因此,我们可以使用
stloc.sv_4
而不是
stloc.sv_4
,它只有
0x13 0x4
,因此更小

还有一些变体在指令中包含特定值。因此,我们不必使用
stloc V_0
stloc.s V_0
而只需使用
stloc.0
,它只是单字节
0x0A

这是非常有意义的,当你认为一次只使用少量的本地人是很常见的,所以使用<代码> STLC.S/<代码>或(更好)类似的代码> STLC.0代码/代码>代码> STLC.1代码>等)给出了微小的节省,总计达很多。 但仅此而已。如果我们有例如

stloc.252
stloc.253
等,那么就会有很多这样的指令,每条指令所需的字节数将不得不更多,而且总体上是一种损失。本地相关(
stloc
ldloc
)和参数相关(
ldarg
)的超短形式仅适用于
3
。(有
starg
starg.s
但没有
starg.0
等,因为存储到参数相对较少)
ldc.i4
/
ldc.i4.s
(将一个恒定的32位有符号值推到堆栈上)具有超短版本,从
ldc.i4.0
ldc.i4.8
,以及
-1
lcd.i4.m1

还值得注意的是,
V_4
根本不存在于代码中。无论您使用什么工具检查IL,都不知道您使用了变量名
name
,因此它只使用了
V_4
。(顺便说一句,你在用什么?我大部分都用,如果你调试与文件相关的信息,它会相应地调用它
name

因此,为了生成一个带有更多可比名称的方法的非简短注释版本,我们可以编写以下CIL:

.method public hidebysig static void  Main() cil managed
{
  .entrypoint
  .maxstack  1
  .locals init (int32 unassigned,
           int32 i,
           float64 d,
           float64 PI,
           string name)
  nop                           // Do Nothing (helps debugger to have some of these around).
  ldc.i4   4                    // Push number 4 on stack
  stloc    i                    // Pop value from stack, put in i (i = 4)
  ldloc    i                    // Push value in i on stack
  stloc    unassigned           // Pop value from stack, put in unassigned (unassigned = i)
  ldc.r8   12.34                // Push the 64-bit floating value 12.34 onto the stack
  stloc    d                    // Push the value on stack in d (d = 12.34)
  ldc.r8   3.1415926535897931   // Push the 64-bit floating value 3.1415926535897931 onto the stack.
  stloc PI                      // Pop the value from stack, put in PI (PI = 3.1415… which is the constant Math.PI)
  ldstr    "Ehsan"              // Push the string "Ehsan" on stack
  stloc    name                 // Pop the value from stack, put in name
  ret                           // return.
}
这将与您的代码的行为非常相似,但要大一点。因此,我们可以用
stloc.0
替换
stloc.3
,用
stloc.s
替换
stloc.s
,在那里我们不能使用它们,但仍然可以使用
stloc.s
,用
ldc.i4.4
替换
ldc.i4
,我们将使用更短的字节码来完成同样的任务:

.method public hidebysig static void  Main() cil managed
{
  .entrypoint
  .maxstack  1
  .locals init (int32 unassigned,
           int32 i,
           float64 d,
           float64 PI,
           string name)
  nop                           // Do Nothing (helps debugger to have some of these around).
  ldc.i4.4                      // Push number 4 on stack
  stloc.1                       // Pop value from stack, put in i (i = 4)
  ldloc.1                       // Push value in i on stack
  stloc.0                       // Pop value from stack, put in unassigned (unassigned = i)
  ldc.r8   12.34                // Push the 64-bit floating value 12.34 onto the stack
  stloc.2                       // Push the value on stack in d (d = 12.34)
  ldc.r8   3.1415926535897931   // Push the 64-bit floating value 3.1415926535897931 onto the stack.
  stloc.3                       // Pop the value from stack, put in PI (PI = 3.1415… which is the constant Math.PI)
  ldstr    "Ehsan"              // Push the string "Ehsan" on stack
  stloc.s  name                 // Pop the value from stack, put in name
  ret                           // return.
}
现在我们有了与反汇编完全相同的代码,只是我们有了更好的名称。请记住,名称不会出现在字节码中,因此反汇编程序不能像我们一样做好工作


你在评论中的问题实际上应该是另一个问题,但它提供了一个机会来补充一些重要的东西,我只是在上面简要地提到过。让我们考虑一下:

public static void Maybe(int a, int b)
{
  if (a > b)
    Console.WriteLine("Greater");
  Console.WriteLine("Done");
}
在debug中编译,最终得到如下结果:

.method public hidebysig static 
  void Maybe (
    int32 a,
    int32 b
  ) cil managed 
{
  .maxstack 2
  .locals init (
    [0] bool CS$4$0000
  )

  IL_0000: nop
  IL_0001: ldarg.0
  IL_0002: ldarg.1
  IL_0003: cgt
  IL_0005: ldc.i4.0
  IL_0006: ceq
  IL_0008: stloc.0
  IL_0009: ldloc.0
  IL_000a: brtrue.s IL_0017

  IL_000c: ldstr "Greater"
  IL_0011: call void [mscorlib]System.Console::WriteLine(string)
  IL_0016: nop

  IL_0017: ldstr "Done"
  IL_001c: call void [mscorlib]System.Console::WriteLine(string)
  IL_0021: nop
  IL_0022: ret
}
现在需要注意的是,所有标签(如
il0017
等)都是根据指令的索引添加到每一行的。这使反汇编程序的工作更轻松,但实际上并不需要,除非跳转到标签。让我们去掉所有未跳转到的标签:

.method public hidebysig static 
  void Maybe (
    int32 a,
    int32 b
  ) cil managed 
{
  .maxstack 2
  .locals init (
    [0] bool CS$4$0000
  )

  nop
  ldarg.0
  ldarg.1
  cgt
  ldc.i4.0
  ceq
  stloc.0
  ldloc.0
  brtrue.s IL_0017

  ldstr "Greater"
  call void [mscorlib]System.Console::WriteLine(string)
  nop

  IL_0017: ldstr "Done"
  call void [mscorlib]System.Console::WriteLine(string)
  nop
  ret
}

现在,让我们考虑每一行所做的事情:

.method public hidebysig static 
  void Maybe (
    int32 a,
    int32 b
  ) cil managed 
{
  .maxstack 2
  .locals init (
    [0] bool CS$4$0000
  )

  nop                   // Do nothing
  ldarg.0               // Load first argument (index 0) onto stack.
  ldarg.1               // Load second argument (index 1) onto stack.
  cgt                   // Pop two values from stack, push 1 (true) if the first is greater
                        // than the second, 0 (false) otherwise.
  ldc.i4.0              // Push 0 onto stack.
  ceq                   // Pop two values from stack, push 1 (true) if the two are equal,
                        // 0 (false) otherwise.
  stloc.0               // Pop value from stack, store in first local (index 0)
  ldloc.0               // Load first local onto stack.
  brtrue.s IL_0017      // Pop value from stack. If it's non-zero (true) jump to IL_0017

  ldstr "Greater"       // Load string "Greater" onto stack.

                        // Call Console.WriteLine(string)
  call void [mscorlib]System.Console::WriteLine(string)
  nop                   // Do nothing

  IL_0017: ldstr "Done" // Load string "Done" onto stack.
                        // Call Console.WriteLine(string)
  call void [mscorlib]System.Console::WriteLine(string)
  nop                   // Do nothing
  ret                   // return
}
让我们一步一步地将其写回C#:

public static void Maybe(int a, int b)
{
  bool shouldJump = (a > b) == false;
  if (shouldJump) goto IL_0017;
  Console.WriteLine("Greater");
IL_0017:
  Console.WriteLine("Done");
}
试试看,你会发现它也有同样的效果。之所以使用
goto
,是因为CIL实际上没有类似
for
的东西,而
甚至是我们可以在
之后放置的块<
.method public hidebysig static 
  void Maybe (
    int32 a,
    int32 b
  ) cil managed 
{
  .maxstack 2
  .locals init (
    [0] bool CS$4$0000
  )

  nop
  ldarg.0
  ldarg.1
  cgt
  ldc.i4.0
  ceq
  stloc.0
  ldloc.0
  brtrue.s IL_0017

  ldstr "Greater"
  call void [mscorlib]System.Console::WriteLine(string)
  nop

  IL_0017: ldstr "Done"
  call void [mscorlib]System.Console::WriteLine(string)
  nop
  ret
}
.method public hidebysig static 
  void Maybe (
    int32 a,
    int32 b
  ) cil managed 
{
  .maxstack 2
  .locals init (
    [0] bool CS$4$0000
  )

  nop                   // Do nothing
  ldarg.0               // Load first argument (index 0) onto stack.
  ldarg.1               // Load second argument (index 1) onto stack.
  cgt                   // Pop two values from stack, push 1 (true) if the first is greater
                        // than the second, 0 (false) otherwise.
  ldc.i4.0              // Push 0 onto stack.
  ceq                   // Pop two values from stack, push 1 (true) if the two are equal,
                        // 0 (false) otherwise.
  stloc.0               // Pop value from stack, store in first local (index 0)
  ldloc.0               // Load first local onto stack.
  brtrue.s IL_0017      // Pop value from stack. If it's non-zero (true) jump to IL_0017

  ldstr "Greater"       // Load string "Greater" onto stack.

                        // Call Console.WriteLine(string)
  call void [mscorlib]System.Console::WriteLine(string)
  nop                   // Do nothing

  IL_0017: ldstr "Done" // Load string "Done" onto stack.
                        // Call Console.WriteLine(string)
  call void [mscorlib]System.Console::WriteLine(string)
  nop                   // Do nothing
  ret                   // return
}
public static void Maybe(int a, int b)
{
  bool shouldJump = (a > b) == false;
  if (shouldJump) goto IL_0017;
  Console.WriteLine("Greater");
IL_0017:
  Console.WriteLine("Done");
}
.method public hidebysig static 
  void Maybe (
    int32 a,
    int32 b
  ) cil managed 
{
  ldarg.0           // Load first argument onto stack
  ldarg.1           // Load second argument onto stack
  ble.s IL_000e     // Pop two values from stack. If the first is
                    // less than or equal to the second, goto IL_000e: 
  ldstr "Greater"   // Load string "Greater" onto stack.
                    // Call Console.WriteLine(string)
  call void [mscorlib]System.Console::WriteLine(string)
                    // Load string "Done" onto stack.
  IL_000e: ldstr "Done"
                    // Call Console.WriteLine(string)
  call void [mscorlib]System.Console::WriteLine(string)
  ret
}
public static void Maybe(int a, int b)
{
  if (a <= b) goto IL_000e;
  Console.WriteLine("Greater");
IL_000e:
  Console.WriteLine("Done");
}