我对.NET语言环境中的机器代码和本机代码感到困惑。
他们之间有什么区别?它们是一样的吗?
答案 0 :(得分:136)
这些术语确实有点令人困惑,因为它们有时使用不一致。
机器代码:这是定义最明确的代码。它是使用您的处理器(执行实际工作的物理金属片)直接理解和执行的字节码指令的代码。在您的机器执行之前,必须将所有其他代码翻译或转换为机器代码。
原生代码:此术语有时用于机器代码(见上文)的地方。但是,它有时也用于表示非托管代码(见下文)。
非托管代码和托管代码: 非托管代码是指用C或C ++等编程语言编写的代码,直接编译进入机器代码。它与托管代码形成对比,后者用C#,VB.NET,Java或类似代码编写,并在虚拟环境(如.NET或JavaVM)中执行,这种环境是“模拟”的处理器在软件中。主要区别在于托管代码通过使用垃圾收集和保持对不透明对象的引用来“管理”资源(主要是内存分配)。 非托管代码是一种代码,需要您手动分配和取消分配内存,有时会导致内存泄漏(当您忘记取消分配时),有时会出现分段错误(当您解除分配时)不久)。 Unmanaged 通常也意味着没有针对常见错误的运行时检查,例如空指针解除引用或数组边界溢出。
严格地说,大多数动态类型的语言 - 例如Perl,Python,PHP和Ruby--也是托管代码。但是,它们并不常见,因此表明托管代码实际上是一个非常重要的商业编程环境(.NET和Java)的营销术语。
汇编代码:这个术语通常是指人们在真正想要编写字节码时编写的源代码。 汇编程序是一个将此源代码转换为实际字节代码的程序。它不是编译器,因为转换是1对1。但是,对于使用何种字节代码,该术语含糊不清:它可以是托管代码,也可以是非托管代码。如果它不受管理,则生成的字节代码为机器代码。如果它被管理,它将导致虚拟环境(如.NET)在幕后使用的字节码。托管代码(例如C#,Java)被编译成这种特殊的字节代码语言,在.NET的情况下称为通用中间语言(CIL),在Java中称为 Java字节-code 。通常程序员通常不需要访问此代码或直接使用这种语言编写,但是当人们这样做时,他们经常将其称为汇编代码,因为他们使用汇编程序< / em>将其转换为字节码。
答案 1 :(得分:42)
在调试C#程序时使用Debug + Windows + Disassembly时所看到的内容是这些术语的一个很好的指南。当我在Release配置中编译一个用C#编写的'hello world'程序并启用了JIT优化时,这是一个带注释的版本:
static void Main(string[] args) {
Console.WriteLine("Hello world");
00000000 55 push ebp ; save stack frame pointer
00000001 8B EC mov ebp,esp ; setup current frame
00000003 E8 30 BE 03 6F call 6F03BE38 ; Console.Out property getter
00000008 8B C8 mov ecx,eax ; setup "this"
0000000a 8B 15 88 20 BD 02 mov edx,dword ptr ds:[02BD2088h] ; arg = "Hello world"
00000010 8B 01 mov eax,dword ptr [ecx] ; TextWriter reference
00000012 FF 90 D8 00 00 00 call dword ptr [eax+000000D8h] ; TextWriter.WriteLine()
00000018 5D pop ebp ; restore stack frame pointer
}
00000019 C3 ret ; done, return
右键单击窗口并勾选“显示代码字节”以获得类似的显示。
左侧的列是机器代码地址。它的值是由调试器伪造的,代码实际上位于其他地方。但这可能是任何地方,取决于JIT编译器选择的位置,因此调试器只是在方法开始时从0开始编号地址。
第二列是机器代码。 CPU执行的实际1和0。像这里的机器代码通常以十六进制显示。说明可能是0x8B选择MOV指令,附加字节用于告诉CPU究竟需要移动什么。另请注意CALL指令的两种风格,0xE8是直接调用,0xFF是间接调用指令。
第三列是汇编代码。汇编是一种简单的语言,旨在使编写机器代码更容易。它与编译为IL的C#相比。用于转换汇编代码的编译器称为“汇编程序”。您的计算机上可能有Microsoft汇编程序,其可执行文件名为ml.exe,64位版本为ml64.exe。使用的汇编语言有两种常见版本。您看到的是英特尔和AMD使用的那个。在开源世界中,AT&amp; T表示法中的组装是常见的。语言语法在很大程度上取决于编写的CPU类型,PowerPC的汇编语言非常不同。
好的,这可以解决你问题中的两个术语。 “本机代码”是一个模糊术语,它并不常用于描述非托管语言中的代码。指导性的可能是看看C编译器生成了什么样的机器代码。这是C语言中的“hello world”版本:
int _tmain(int argc, _TCHAR* argv[])
{
00401010 55 push ebp
00401011 8B EC mov ebp,esp
printf("Hello world");
00401013 68 6C 6C 45 00 push offset ___xt_z+128h (456C6Ch)
00401018 E8 13 00 00 00 call printf (401030h)
0040101D 83 C4 04 add esp,4
return 0;
00401020 33 C0 xor eax,eax
}
00401022 5D pop ebp
00401023 C3 ret
我没有注释它,主要是因为它与C#程序生成的机器代码非常相似。 printf()函数调用与Console.WriteLine()调用完全不同,但其他所有内容大致相同。另请注意,调试器现在生成真实的机器代码地址,并且它对符号更加智能。 生成机器代码之后生成调试信息的副作用通常是非托管编译器。我还要提一下,我关闭了一些机器代码优化选项,使机器代码看起来很相似。 C / C ++编译器有更多时间可用于优化代码,结果通常难以解释。并且非常难以调试。
这里的关键点是,JIT编译器从托管语言生成的机器代码与本机代码编译器生成的机器代码之间存在非常的差异。这是C#语言与本机代码编译器竞争的主要原因。它们之间唯一真正的区别是支持函数调用。其中许多是在CLR中实现的。而这主要围绕着垃圾收集器。
答案 2 :(得分:5)
本机代码和机器代码是相同的 - CPU执行的实际字节。
汇编代码有两个含义:一个是将机器代码翻译成更易于阅读的形式(将指令的字节翻译成类似“JMP”的短字类助记符(“跳转”到代码中的另一个位置)另一个是IL字节码(编译器如C#或VB生成的指令字节,最终将最终转换为机器代码,但尚未生成DLL或EXE)。
答案 3 :(得分:2)
在.NET中,程序集包含MS Intermediate Language代码(MSIL,有时是CIL) 它就像一个“高级”机器代码。
加载时,MSIL由JIT compiler编译为本机代码(Intel x86或x64机器代码)。