编译背后的主要步骤是什么?

Pab*_*uez 3 c compiler-construction assembly gcc compilation

编译C程序的主要步骤是什么?通过编译,我的意思是(可能是错误的)使用gcc从包含C代码的纯文本中获取二进制文件.

我很想了解这个过程的一些关键点:

  1. 到那天结束时,我需要将我的C代码转换为我的CPU应该理解的语言.那么,谁在乎了解我的CPU特定指令呢?操作系统?

  2. gcc是否将任何C转换为汇编语言?

  3. 我知道(实际猜测)对于每种处理器类型,我将需要一个汇编程序来解释(?)汇编代码并转换为我的CPU特定指令.这个汇编程序(谁发货)在哪里?它是否附带操作系统?

  4. 如果我用文本编辑器打开二进制文件,为什么我看不到0和1?

thu*_*zas 9

发生了很多:)

以下是一些关键步骤(顺便说一句,这些是我对编译的看法,以下步骤只与标准中定义的步骤有相似之处).

  1. 预处理器对源文件运行.

    预处理器为我们做了各种各样的事情,包括:

    • 它执行三字形(特殊的三个字符序列,代表早期键盘没有的一些特殊符号)替换.
    • 它通过简单的文本替换执行宏替换(即#define)
    • 它抓取任何头文件并将其全部内容复制到该#include行所在的位置.

    在Linux下,执行此操作的程序是m4,使用gcc您可以在此步骤之后使用该-E标志停止.

  2. 在预处理器运行之后,我们有一个文件,其中包含解析器运行和检查语法所需的所有信息,并发出汇编.在Linux下,最有可能执行此操作的程序是cc1,使用gcc-s标志可以在此步骤之后停止使用.

  3. 程序集(GNU Assembler)很可能将程序集转换为目标代码,使用此标志可以在此步骤中停止.gasgcc-c

  4. 最后,链接器将一个或多个目标文件以及库转换为可执行文件.Linux下的链接器是正常的,并且在没有任何特殊标志的情况下使用它会一直运行.ldgcc


jac*_*bit 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 .当解析器感觉它足够了解时,它可以在让词法分析器为它产生更多标记之前进行一些代码生成.