汇编代码 vs 机器代码 vs 目标代码?

281

目标代码、机器代码和汇编代码之间有什么区别?

你能举一个视觉上的例子吗?


3
我也很好奇“目标代码”这个名字来自哪里? “对象”这个词在其中意味着什么? 它与面向对象编程有关还是只是名称的巧合? - SasQ
1
@SasQ:目标代码 - Jesse Good
2
我不是在问什么是目标代码,显而易见的船长。我在问这个名字从哪里来,为什么叫做“目标”代码。 - BarbaraKwarc
我认为这是目标代码,因为它是一个对象,链接器可以使用它与其他对象链接以生成机器代码(可执行文件或共享库)。 - Maf
10个回答

358

机器码是二进制(1和0)代码,可以直接由CPU执行。如果在文本编辑器中打开机器码文件,您会看到垃圾字符,包括不可打印的字符(不是那些不可打印的字符 ;) )。

目标码是尚未链接到完整程序中的部分机器码。它是构成完成产品的一个特定库或模块的机器码。它还可能包含占位符或偏移量,在已经完成的程序的机器码中找不到。链接器将使用这些占位符和偏移量连接所有内容。

汇编代码是纯文本且(有点)易于阅读的源代码,其大部分具有与机器指令直接对应的1:1模拟。这是通过为实际指令、寄存器或其他资源使用助记符来实现的。例如CPU的跳转和乘法指令的JMPMULT。与机器码不同,CPU不理解汇编代码。您可以借助汇编器编译器将汇编代码转换为机器代码,尽管我们通常考虑编译器与更高级的编程语言相关联,这些语言进一步抽象了CPU指令。


构建完整程序涉及使用汇编语言或像C++这样的更高级别语言为程序编写源代码。将源代码汇编(对于汇编代码)或编译(对于更高级别语言)为目标码,然后将单独的模块链接在一起,成为最终程序的机器码。对于非常简单的程序,可能不需要连接步骤。在其他情况下,例如使用IDE(集成开发环境),链接器和编译器可以一起调用。在其他情况下,可以使用复杂的make脚本或solution文件告诉环境如何构建最终应用程序。

还有一种解释型语言,其行为方式不同。解释型语言依靠特殊解释器程序的机器码。在基本层面上,解释器解析源代码并立即将命令转换为新的机器码并执行它们。现代解释器现在更加复杂:一次性评估整个源代码部分,尽可能缓存和优化,并处理复杂的内存管理任务。

最后一种程序类型涉及使用运行环境虚拟机。在这种情况下,程序首先被预编译成较低级别的中间语言字节码。然后,虚拟机加载字节码,并即时将其编译成本地代码。这里的优势在于虚拟机可以利用程序运行时和特定环境提供的优化。编译器属于开发人员,因此必须生成相对通用(不太优化)的机器码,可在许多地方运行。但是,运行时环境或虚拟机位于最终用户的计算机上,因此可以利用该系统提供的所有功能。


35
+1: 很好,但是有点简化了回答 - 并非所有的汇编指令都可以一对一地转换为机器指令,而且目标文件中还可能包含其他数据(如重定位信息、符号表等...)。 - Christoph
7
为您的第一个问题添加了一个模棱两可的词语,编辑后使第二个问题更清晰。 - Joel Coehoorn
4
@Christoph:您说“并非所有汇编指令都可以一对一地转换为机器指令”,请举一个例子。 请给出一个例子。 - Olof Forshell
7
RISC架构有时会提供汇编级别的虚拟指令集,例如MIPS伪指令。您可以参考维基百科的MIPS架构页面中的相关介绍:http://en.wikipedia.org/wiki/MIPS_architecture#Pseudo_instructions 。 - Christoph
4
@Panzercrisis,汇编语言不会添加任何内容,它直接将你所写的内容翻译为机器指令。同时我认为编译器添加的额外代码并不是“不必要的”。 - Joel Coehoorn
显示剩余12条评论

151

其他回答已经很好地描述了它们之间的区别,但您还希望有一个可视化展示。以下是一张图表,展示了从C代码到可执行文件的过程。


7
我觉得这个非常有帮助,但是缺少“机器码”标签。 - Alexx Roche
那么,当它处于可执行代码级别时,是否相当于机器码? - CMCDragonkai
4
这个图中的“目标代码”指的就是机器码。 - Graphics Noob
12
实际上,目标代码和可执行代码都是机器码。区别在于,目标代码不是完整的程序。它需要与图示中指示的其他辅助库/模块代码组合,形成完整的可执行程序/代码。 - okey_on
@okeyxyz,在哪个层次上才能正确地说它是由处理器直接执行的?汇编器之后,链接器之后,装载器之后,还是在转换为微控制器之后? - Celeritas

58

汇编代码是机器码的人类可读表示:

mov eax, 77
jmp anywhere

机器代码是纯十六进制代码:

5F 3A E3 F1

我理解你所说的目标代码是指目标文件中的对象代码。与机器代码相比,它的跳转有点参数化,以便链接器可以填充它们。

汇编器用于将汇编代码转换为机器代码(目标代码)。链接器将多个对象(和库)文件链接在一起生成可执行文件。

我曾经使用纯十六进制编写过汇编程序(没有可用的汇编器),幸运的是这是在早期的 6502 处理器上完成的。但我很高兴现在有针对 Pentium 指令集的汇编器。


91
不,不,不,不。机器码并不是十六进制代码,它是纯二进制的。十六进制代码只是二进制的一种便捷表示方式。 - Breton
72
如果我们真的要进入极端状态,那么它并不是二元的,而是电路中储存的电量。;-) - Toon Krijthe
22
当然可以。十六进制和你所谓的“机器码”之间存在着关联,但是说十六进制就等同于机器码并不完全准确。这就是我想要表达的全部内容。 - Breton
12
在这个意义上,"十六进制代码"并不存在,对于"十六进制代码"只是一种查看机器码的方式。你可以用十六进制、二进制、八进制、十进制等任何方式来查看机器码。同样地,在这个意义上也不存在"二进制代码"。再次强调,"二进制代码"只是一种查看机器码的方式。 - Utku
11
@Breton,您说的话并没有太多意义。二进制是一种表示方式,就像十六进制一样。如果不是十六进制,它也不是二进制。 - Koray Tugay
显示剩余5条评论

23

8B 5D 32 是机器码。

mov ebx, [ebp+32h] 是汇编语言。

lmylib.so 包含 8B 5D 32,是目标代码。


十六进制并不是真正的机器码,而只是一种更简便的表示方式。 - madladzen
我认为这只是二进制被翻译成不同数量的电,但我不确定。我只知道十六进制不是实际的机器码,就像用英语表示C++一样。 - madladzen
@madladzen 实际上你可以说十六进制是机器码.. 十六进制、二进制,它们实际上是相同的,简单地说。虽然你可以用十进制表示它,但这并不方便,因为它不是2^N。 - starriet

12

源代码、汇编代码、机器代码、目标代码、字节码、可执行文件和库文件。所有这些术语通常会让大多数人感到困惑,因为他们认为这些术语是相互独立的。请参考下图以了解它们之间的关系。每个术语的描述如下。


Types of code


源代码

用人类可读(编程)语言编写的指令


高级代码

用高级(编程)语言编写的指令
例如 C、C++ 和 Java 程序。


汇编代码

用汇编语言编写的指令(一种低级编程语言)。作为编译过程的第一步,将高级代码转换为此形式。随后进行的是将汇编代码转换为实际的机器代码。在大多数系统上,这两个步骤是作为编译过程的一部分自动完成的。
例如 program.asm。


目标代码

编译过程的产物。它可以是机器代码或字节码的形式。
例如 file.o。


机器代码

用机器语言写成的指令。
例如 a.out。


字节码

以一种中间形式编写的指令,可以由JVM等解释器执行。


可执行文件

链接过程的产物,是机器码,可以直接由CPU执行。
例如,.exe文件。

需要注意的是,在某些情况下,包含字节码或脚本语言指令的文件也可能被认为是可执行文件。


库文件

某些代码以此形式编译出来,出于不同的原因,例如可重用性,并且稍后由可执行文件使用。


1
我认为,并非所有的汇编语言都是严格意义上由人类编写和/或维护的“源代码”。通常情况下,它是从源代码生成的机器代码,从未被设计用于人类阅读(例如,gcc确实创建了asm文本,将其提供给单独的汇编器,而不是在cc1可执行文件中内置汇编器)。我认为汇编圆圈应该突出“源”圆圈的左侧,因为有些汇编只是汇编,而不是源代码。当然,它永远不会是目标代码,但某些汇编是从源代码到目标文件的过程中的一步。 - Peter Cordes
@PeterCordes非常感谢您的评论。我不知道您所说的gcc的工作方式。然而,我不确定我是否完全同意您的观点。我的意思是,源代码是使用人类可读的编程语言编写的东西。它可能是由人编写或维护的,也可能不是。我相信您会知道转译器。从您的角度来看,您会将这种编译器的产品归为哪一类?源代码还是其他什么?如果我错了,请纠正我。欢迎进一步评论。 - Bertram Gilfoyle
机器生成的任何语言代码通常不被视为“源代码”。例如,GUI构建器可能会发出一堆C++代码来实现按钮处理程序,虽然你可以手动编辑它,但这不是可维护性的好起点。编译器生成的汇编文本也是如此。或者例如,C预处理器的输出也是C,但不是可维护的C。因此,是的,您的Venn图可能有第三个类别:机器生成的文本作为从真正的人工编辑的源代码到目标代码的编译过程中的中间产品。 - Peter Cordes
但是,“源代码”一词的另一个定义可能包括任何文本语言。您确实可以使用编译器输出作为手写汇编函数的起点,只需添加一些注释,给标签赋予有意义的名称等即可。因此,没有明确的分界线。(我不知道我是否错过了您一年前的评论,只是现在碰巧看到了它。) - Peter Cordes

10

还没有提到的一个问题是汇编代码有几种不同类型。在最基本的形式中,指令中使用的所有数必须被指定为常量,例如:

$1902: BD 37 14 : LDA $1437,X
$1905: 85 03    : STA $03
$1907: 85 09    : STA $09
$1909: CA       : DEX
$190A: 10       : BPL $1902

如果将上面这段代码存储在Atari 2600游戏卡带中地址为$1900的位置,它将从地址为$1437的表中获取多个颜色的行并以不同的颜色显示。在某些工具中,键入一个地址以及上述行的最右侧部分将会将中间一列显示的值存储到内存中,并以下一个地址开始下一行。使用这种形式的代码比输入十六进制代码更方便,但需要知道每个东西的精确地址。

大多数汇编器都允许使用符号地址。上面的代码可以写成:

rainbow_lp:
  lda ColorTbl,x
  sta WSYNC
  sta COLUBK
  dex
  bpl rainbow_lp

汇编器将自动调整LDA指令,使其引用映射到标签ColorTbl的任何地址。使用这种样式的汇编器使得编写和编辑代码比手动输入和维护所有地址更容易。


1
另外需要补充一点:汇编语言也有不同的语法,其中最著名的是Intel和AT&T。详见:http://en.wikipedia.org/wiki/X86_assembly_language#Syntax 和 http://www.imada.sdu.dk/Courses/DM18/Litteratur/IntelnATT.htm。 - informatik01
1
@informatik01:Intel 8080助记符和Zilog Z80怎么样?我猜这比Intel vs AT&T语法战还要早。 - supercat
不是在争论,我只是提到了这个方面(不同的语法),并且给出了两种最流行/最知名/最著名的语法的例子。 - informatik01

3
汇编语言是人类能够理解的简短描述术语,可以直接转换为CPU实际使用的机器代码。
虽然汇编语言对人类来说还算容易理解,但它仍然是底层的。要做任何有用的事情,需要大量的代码。
因此,我们使用更高级的语言,如C、BASIC、FORTAN(好吧,我知道我已经过时了)。编译后,这些语言会产生目标代码。早期的语言的目标代码是机器语言。
今天许多语言,如JAVA和C#,通常会编译成字节码,而不是机器代码,但可以在运行时轻松地解释成机器代码。

你对Java和C#的评论 - 它们都使用即时编译,以便字节码不被解释。 C#(一般为.NET)编译为中间语言(IL),然后JIT为目标CPU生成本机机器语言。 - Craig Shearer

2

讨论汇编语言可以在这里找到。

"汇编语言是一种用于编程计算机的低级语言。它实现了一种符号表示,用于编程特定的CPU架构所需的数值机器代码和其他常量。"

机器码可以在这里找到。

"机器码或机器语言是一种由计算机的中央处理单元直接执行的指令和数据系统。"

基本上,汇编器代码是语言,通过汇编器(类似于编译器)将其转换为目标代码(CPU运行的本地代码)。


2
我认为这些是主要的区别:
  • 代码可读性
  • 对代码的控制
可读性可以使得代码在创建后六个月内进行改进或替换,而不需要太多的努力。另一方面,如果性能很关键,您可能希望使用低级语言来针对生产中将要使用的特定硬件,以获得更快的执行速度。
在我看来,现今的计算机已经足够快了,让程序员可以通过面向对象编程获得快速的执行。

0

您的程序源文件将被编译成目标文件,然后链接器将这些目标文件链接在一起,生成一个包含您架构机器代码的可执行文件。

无论是目标文件还是可执行文件,在文本编辑器中打开时都涉及到架构机器代码的可打印和不可打印字符形式。

尽管如此,文件之间的区别在于目标文件可能包含未解决的外部引用(例如printf)。因此,它可能需要与其他目标文件链接。也就是说,需要解决未解决的外部引用,才能通过与其他目标文件(如C/C++运行时库)链接获得良好的可运行可执行文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接