ChatGPT解决这个技术问题 Extra ChatGPT

本机代码、机器代码和汇编代码有什么区别?

我对 .NET 语言上下文中的机器代码和本机代码感到困惑。

它们之间有什么区别?他们是一样的吗?

我有一个关于这个问题的问题。这个问题是否符合 StackOverflow 的要求? afaik 不是,但同时这种问题非常有帮助/信息丰富。假设不允许这种类型的问题,如果这里没有,我们应该在哪里问这些类型的问题?

T
Timwi

这些术语确实有点令人困惑,因为它们有时使用不一致。

机器码:这是定义最明确的一种。它是使用您的处理器(执行实际工作的物理金属片)理解并直接执行的字节码指令的代码。所有其他代码必须先翻译或转换为机器代码,然后您的机器才能执行它。

本机代码:该术语有时用于表示机器代码(见上文)的地方。但是,它有时也用于表示非托管代码(见下文)。

非托管代码和托管代码:非托管代码是指用C或C++等编程语言编写的代码,直接编译成机器码。它与使用 C#、VB.NET、Java 或类似语言编写并在虚拟环境(例如 .NET 或 JavaVM)中执行的托管代码形成对比,后者在软件中“模拟”处理器。主要区别在于托管代码通过使用垃圾收集和保持对对象的不透明引用来为您“管理”资源(主要是内存分配)。非托管代码是一种需要您手动分配和取消分配内存的代码,有时会导致内存泄漏(当您忘记取消分配时),有时会导致分段错误(当您过早取消分配时)。非托管通常还意味着没有运行时检查常见错误,例如空指针取消引用或数组边界溢出。

严格来说,大多数动态类型语言——例如 Perl、Python、PHP 和 Ruby——也是托管代码。但是,它们通常不被这样描述,这表明托管代码实际上在某种程度上是一个营销术语,用于真正大型、严肃的商业编程环境(.NET 和 Java)。

汇编代码:这个术语通常是指人们在真正想要编写字节码时编写的那种源代码。汇编器是将源代码转换为真正的字节码的程序。它不是编译器,因为转换是一对一的。但是,该术语对于使用哪种字节码是模棱两可的:它可以是托管的或非托管的。如果它是非托管的,则生成的字节码是机器码。如果它被管理,它会导致字节码被虚拟环境(如 .NET)在幕后使用。托管代码(例如 C#、Java)被编译成这种特殊的字节码语言,在 .NET 中称为通用中间语言 (CIL),在 Java 中称为 Java 字节码。普通程序员通常很少需要访问此代码或直接用这种语言编写,但是当人们这样做时,他们通常将其称为汇编代码,因为他们使用汇编程序将其转换为字节码。


C++ 可以编译为机器代码,但它经常被编译为其他格式,例如可以在操作系统上运行的 exe。
有些语言确实支持垃圾收集和通常编译为机器代码的不透明引用。 Common Lisp 的大多数严肃实现都是这样做的。您所说的对于 Microsoft 支持的语言可能是正确的,但编译语言的数量比 Visual Studio 支持的要多。
@CrazyJugglerDrummer:C++ 编译器生成的 EXE 文件中包含的代码仍然是机器码。 @David Thornley:我提到的语言远不止这些语言,但我不想通过提及每一个晦涩难懂的奇怪事物来使事情复杂化。
许多编译器实际上会从 C/C++ 或其他语言编译为汇编语言,然后调用汇编器,汇编器将其转换为目标文件,这些目标文件主要是机器代码,但在它们进入处理器的内存之前需要进行一些操作链接器将所有这些链接到程序的机器代码版本中。关键是 C/C++ 等通常不会直接编译成机器代码,它对用户不可见,需要执行两到三步。例如,TCC 是一个例外,它确实直接进入机器代码。
这感觉像是在吹毛求疵,但并非所有汇编程序都将 1-1 转换为操作码。事实上,许多现代汇编器都支持类等抽象结构。示例:TASM,Borland 的汇编程序。 en.wikipedia.org/wiki/TASM
H
Hans Passant

在调试 C# 程序时使用 Debug + Windows + Disassembly 时所看到的内容是这些术语的良好指南。这是我在启用 JIT 优化的发布配置中编译用 C# 编写的“hello world”程序时的注释版本:

        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,ml64.exe 为 64 位版本。有两种常用的汇编语言版本正在使用中。你看到的是英特尔和 AMD 使用的那个。在开源世界中,AT&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 中实现的。这主要围绕垃圾收集器展开。


c
cHao

本机代码和机器代码是一回事——CPU 执行的实际字节。

汇编代码有两种含义:一种是将机器代码翻译成更易于人类阅读的形式(指令的字节被翻译成类似“JMP”的短单词助记符(它“跳转”到代码中的另一个位置))。另一种是存在于 DLL 或 EXE 中的 IL 字节码(C# 或 VB 等编译器生成的指令字节,最终将翻译成机器代码,但还没有)。


这个答案是模棱两可的,并且会歪曲真实的定义
H
Henk Holterman

在 .NET 中,程序集包含 MS Intermediate Language 代码(MSIL,有时是 CIL)。
它就像一个“高级”机器代码。

加载时,MSIL 由 JIT compiler 编译为本机代码(Intel x86 或 x64 机器代码)。