编译C程序的主要步骤是什么?通过编译,我的意思是(可能是错误的)使用gcc从包含C代码的纯文本中获取二进制文件。
我很想了解这个过程的一些关键点:
到一天结束时,我需要将我的C代码转换为我的CPU应该理解的语言。那么,谁在乎了解我的 CPU特定说明?操作系统?
gcc是否将任何C转换为汇编语言?
我知道(实际猜测)对于每种处理器类型,我需要一个汇编程序,它将解释(?)汇编代码并转换为我的CPU特定指令。这个汇编程序(谁发货)在哪里?它是否附带操作系统?
如果用文本编辑器打开二进制文件,为什么我看不到0和1?
答案 0 :(得分:9)
很多事情发生:)
以下是一些关键步骤(顺便说一句,这些是我对编译的看法,以下步骤只与标准中定义的步骤有相似之处)。
预处理器在源文件上运行。
预处理器为我们做了各种各样的事情,包括:
#define
)#include
行所在的位置。在Linux下,执行此操作的程序为m4
,使用gcc
后,您可以使用-E
标记在此步骤后停止。
预处理器运行后,我们有一个文件,其中包含解析器运行和检查语法所需的所有信息,并发出程序集。在Linux下,最有可能执行此操作的程序为cc1
,使用gcc
后,您可以使用-s
标记在此步骤后停止。
程序集很可能由程序gas
(GNU汇编程序)转换为目标代码,使用gcc
可以在此步骤停止使用-c
标志。
最后,链接器将一个或多个目标文件以及库转换为可执行文件。 Linux下的链接器通常为ld
,使用gcc
时没有任何特殊标志会一直运行。
答案 1 :(得分:6)
由于您特别提到'到一天结束时我需要将我的C代码转换为我的CPU应该理解的语言',我将解释一下编译器的工作原理。
典型的编译器做了一些事情。
首先,他们做了一些叫做lexing的事情。这个步骤采用单个字符并将它们组合成“令牌”,这是下一步所理解的事物。此步骤区分语言关键字(如C中的'for'和'if'),运算符(如'+'),常量(如整数和字符串文字)以及其他内容。它的区别究竟取决于语言本身。
下一步是解析器,它获取词法分析器产生的令牌流,并(通常)将其转换为称为“抽象语法树”或AST的东西。 AST表示程序使用编译器可以导航的数据结构进行的计算。通常,AST是与语言无关的,像GCC这样的编译器可以将不同的语言解析为下一步(代码生成器)可以理解的常见AST格式。
最后,代码生成器通过AST并输出代表AST语义的代码,即实际执行AST所代表的计算的代码。
对于GCC,可能还有其他编译器,编译器实际上并不生成机器代码。相反,它输出传递给汇编程序的汇编代码。汇编程序经历了类似的lexing,解析和代码生成过程,以实际生成机器代码。毕竟,汇编程序只是一个编译汇编代码的编译器。
在C(和许多其他人)的情况下,汇编程序通常不是最后一步。汇编程序生成称为目标文件的东西,其中包含对其他目标文件或库中函数的未解析引用(如C标准库中的printf或项目中其他C文件的函数)。这些目标文件被传递给称为“链接器”的东西,它的作用是将所有目标文件组合成一个二进制文件,并解析目标文件中所有未解析的引用。
最后,在完成所有这些步骤之后,您将拥有完整的可执行二进制文件。
请注意,这是GCC和许多其他编译器工作的方式,但并不一定如此。您可以编写的任何程序都准确地接受C代码流并输出一些等效的其他代码(程序集,机器代码,甚至是javascript)的流程,这是一个编译器。
此外,这些步骤并非总是完全分开。而不是lexing和整个文件,然后解析整个结果,然后为整个AST生成代码,编译器可能会做一些lexing,然后在它有一些令牌时开始解析,然后当解析器需要更多令牌时再回到lexing 。当解析器感觉它足够了解时,它可能会在lexer为它生成更多标记之前进行一些代码生成。
答案 2 :(得分:2)
到那天结束时,我需要将我的C代码转换为我的CPU应该理解的语言。那么,谁在乎了解我的CPU特定指令呢?操作系统?
你在这里不是很清楚。如果您在询问哪个工具了解您的CPU特定指令,那么它就是汇编器,反汇编器,调试器以及其他一些工具。他们可以生成机器代码或将其转换回反汇编。
如果你问谁在乎使用哪些指令,那就是需要执行它们的处理器,因为每个指令集甚至代表了以完全不同的方式“添加两个整数”这样的通用指令。
gcc是否将任何C转换为汇编语言?
是的,C(或任何其他支持语言的程序)由GCC转换为汇编。涉及许多步骤,并且在过程中使用至少两个额外的内部表示。详细信息在GCC internals文档中进行了解释。最后,编译器“后端”生成由先前编译器传递生成的简单“模式”的汇编表示。您可以通过使用-S标志要求GCC输出此程序集。如果您没有特别要求,则自动执行下一步(组装),您只能看到最终的可执行文件。
我知道(实际猜测)对于每种处理器类型,我需要一个汇编程序来解释(?)汇编代码并转换为我的CPU特定指令。这个汇编程序(谁发货)在哪里?它是否附带操作系统?
首先请注意,每个CPU的汇编语言不同,因为它们应该代表CPU的机器语言1:1。汇编程序然后将汇编代码转换为机器代码。是谁发货的?建造它的任何人。使用GNU工具链,它是binutils包的一部分,它通常默认安装在大多数Linux发行版上。这不仅是汇编程序。另请注意,虽然GNU“suite”(GCC / binutils / gdb)支持许多体系结构,但您需要为您的体系结构使用适当的端口。例如,桌面PC的默认汇编程序无法编译/汇编为ARM机器代码。
如果用文本编辑器打开二进制文件,为什么我看不到0和1?
因为文本编辑器应该显示该0和1的文本表示。假设文件中的每个字符占用8位,它们将每个后续8位解释为单个字符,而不是显示单独的位。如果你知道在标准的8位ASCII字母'A'用值65表示,你也可以将它转换回二进制:01000001.将十六进制表示转换回二进制更容易一些。为此,您可以使用hexdump(或类似)工具。
答案 3 :(得分:1)
“到一天结束时,我需要将我的C代码转换为我的CPU应该理解的语言。那么,谁在乎了解我的CPU特定指令呢?操作系统?
CPU。
但请注意,在现代计算机上,显然单CPU只是一种幻觉。
这对于简单的C编程来说是一个很好的概念模型。
“ gcc是否将任何C转换为汇编语言?
如果你问它。选项-S
将生成程序集列表。对于PC,你可以选择AT& T语法,这种语法很丑陋,有百分号,还有普通的英特尔语法。不幸的是,AT& T(可通过-masm=att
IIRC选择)是默认设置,但您可以使用-masm=intel
来进行普通组装。
如果你不要求它生成程序集,那么gcc可能直接从其内部抽象语法树(AST)生成目标代码。
生成汇编语言作为中间形式只会增加复杂性和低效率,所以我非常怀疑它是否会这样做。
“我知道(实际猜测)对于每种处理器类型,我需要一个汇编程序来解释(?)汇编代码并转换为我的CPU特定指令。这个汇编程序(谁发货)在哪里?它是否附带操作系统?
您不需要这样的汇编程序。但是gcc附带了汇编程序as
。类Unix操作系统通常捆绑gcc
和as
,而Windows没有捆绑的开发人员工具。然而,微软的开发工具可以免费下载,现在(在过去一周左右),包括完整的Visual Studio IDE。 Microsoft的汇编程序是ml.exe
,称为MASM,宏汇编程序(就好像没有其他宏汇编程序一样)。
“如果我用文本编辑器打开二进制文件,为什么我看不到0和1?
这取决于文本编辑器,虽然我不知道任何可以呈现0和1;文本编辑器旨在将字节解释为文本。
如果需要,您可以编写这样的文本编辑器。
虽然公平警告:它没有我能想到的实际用途。
最后关于标题中的问题,
“编译背后的主要步骤是什么?
在实践中,有两个主要步骤:编译和链接。编译步骤进一步细分为预处理和核心语言编译,即
编译→链接
......真的是
(预处理→核心语言编译)→链接
在预处理期间,源代码文件通过#include
指令组合。这会生成完整的翻译单元源代码。核心语言编译将其转换为目标代码文件,其中包含带有一些未解析引用的机器代码。
然后最后,链接步骤将目标代码文件(包括库中的目标代码文件内容)组合在一起,以创建一个完整的可执行文件。