本机代码,机器代码和汇编代码有什么区别?
我对机器代码和本机代码感到困惑。 这些有什么区别? 这些都一样吗?
这些条款确实有点混乱,因为它们有时被不一致地使用。
机器代码:这是最明确的一个。 使用你的处理器(做实际工作的物理片)直接理解和执行的是字节码指令的代码。 所有其他代码必须在您的机器执行之前转换或转换为机器代码 。
原生代码:这个术语有时用在机器代码 (见上)的地方。 但是,它有时也被用来表示非托pipe代码 (见下文)。
非托pipe代码和托pipe代码: 非托pipe代码是指用编程语言(如C或C ++)编写的代码,直接编译为机器代码 。 它与用C#,VB.NET,Java或类似语言编写的托pipe代码形成对比,并在虚拟环境(如.NET或JavaVM)中执行,这种types的软件可以“模拟”处理器。 主要区别在于, 托pipe代码通过使用垃圾回收和保持对象的引用不透明来“pipe理”资源(主要是内存分配)。 非托pipe代码是一种需要手动分配和取消分配内存的代码,有时会导致内存泄漏(当您忘记取消分配时),有时会出现分段错误(当您取消分配太快时)。 非托pipe也通常意味着没有对常见错误(如空指针解除引用或数组边界溢出)的运行时检查。
严格地说,大多数dynamictypes的语言(如Perl,Python,PHP和Ruby)也是托pipe代码 。 然而,它们通常不是这样描述的,这表明托pipe代码实际上对于真正的大的,严肃的商业编程环境(.NET和Java)而言是一个营销术语。
汇编代码:这个术语通常是指人们真正想写字节码时所写的那种源代码。 汇编程序是将源代码转换为真正的字节码的程序。 它不是编译器,因为转换是1对1的。 然而,这个术语对于使用什么样的字节码是不明确的:它可以被pipe理或者不被pipe理。 如果它是不受pipe理的,则产生的字节码是机器码 。 如果进行pipe理,则会导致由虚拟环境(如.NET)在幕后使用字节码。 被pipe理的代码(例如C#,Java)被编译成这种特殊的字节码语言,在.NET中称为通用中间语言(Common Intermediate Language,CIL) ,在Java中称为Java字节码 。 通常程序员通常很less需要访问这些代码或者直接用这种语言编写代码,但是当人们这样做的时候,他们通常把它称为汇编代码,因为他们使用汇编程序将其转换为字节代码。
在debuggingC#程序时使用Debug + Windows + Disassembly时看到的内容对于这些术语是一个很好的指导。 这里有一个注释版本,当我编译一个用C#编写的“hello world”程序时,在发布configuration中启用了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
右键单击该窗口并勾选“显示代码字节”以获得类似的显示。
左边的列是机器代码地址。 它的值是由debugging器伪造的,代码实际上位于其他地方。 但是这可能在任何地方,取决于JIT编译器select的位置,所以debugging器只是在方法开始时从0开始编号地址。
第二列是机器代码 。 CPU执行的实际1和0。 这里的机器代码通常以hex显示。 说明性的或许是0x8BselectMOV指令,附加字节在那里告诉CPU到底需要移动什么。 还要注意CALL指令的两种风格,0xE8是直接调用,0xFF是间接调用指令。
第三列是汇编代码 。 汇编是一种简单的语言,旨在使机器代码更容易编写。 它与编译为IL的C#相比。 用于翻译汇编代码的编译器称为“汇编器”。 您的计算机上可能有Microsoft汇编程序,其可执行文件名为ml.exe,64位版本为ml64.exe。 有两种常用的汇编语言版本正在使用中。 你看到的是Intel和AMD使用的。 在开源世界中,AT&T符号中的汇编是常见的。 语言语法在很大程度上取决于所编写的CPU的种类,PowerPC的汇编语言是非常不同的。
好的,那就解决你的问题中的两个术语。 “本地代码”是一个模糊的术语,用非托pipe语言描述代码并不罕见。 有教导意义的可能是看C编译器生成的机器代码是什么types的。 这是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()调用完全不同,但其他所有内容大致相同。 另外请注意,debugging器现在正在生成真正的机器代码地址,并且它对符号有点智能。 在生成诸如非托pipe编译器之类的机器代码之后生成debugging信息的副作用通常会发生。 我还应该提到,我closures了几个机器代码优化选项,使机器代码看起来类似。 C / C ++编译器有更多的时间来优化代码,结果往往很难解释。 而且很难debugging。
关键的一点是,JIT编译器从托pipe语言生成的机器代码与本地代码编译器生成的机器代码之间几乎没有区别。 这是C#语言与本地代码编译器竞争的主要原因。 它们之间唯一真正的区别是支持函数调用。 其中许多是在CLR中实施的。 这围绕着垃圾回收器。
本地代码和机器代码是相同的东西 – CPU执行的实际字节。
汇编代码有两个含义:一个是将机器代码翻译成更易于阅读的forms(将指令的字节翻译成如JMP这样的短字形助记符(将其跳转到代码中的另一个位置)是IL字节码(编译器像C#或VB生成的指令字节,最终将最终转换成机器码,但还没有),它们位于DLL或EXE中。
在.NET中,程序集包含MS中间语言代码(MSIL,有时是CIL)。
这就像一个“高级”机器码。
加载时,MSIL由JIT编译器编译为本机代码(Intel x86或x64机器代码)。