1、编译系统

1 编译系统

1.1 引入编译系统

一个简单的 C语言程序:hello.c

#include <stdio.h>
int main(){
    int a=1;
    int b=2;
    int c=a+b;
    printf("hello c\n");
    return c;
}

高级语言:C语言,需要先转换成低级的机器语言,才能被计算机所执行。转换过程,就是编译过程,由编译系统来完成这个转换。

1.2 编译系统的组成

编译系统由预处理器、编译器、汇编器、和链接器四部分组成。如图所示:

高级语言转换成可执行的机器语言,过程经过以上四个阶段。

1.2.1 预处理器

预处理阶段,预处理器根据以字符#开头的命令,修改原始的C程序。例如:#include <stdio.h>命令,这个命令告诉预处理器读取系统头文件stdio.h的内容,并把它直接插入程序文本中。结果就得到了另一个C程序 hello.i.

1.2.2 编译器

编译阶段,编译器将文本文件 hello.i 翻译成文本文件 hello.s ,由C程序变为汇编程序。这里引入汇编语言:是机器语言的文本表示,给出程序中的每一条指令。

1.2.3 汇编器

汇编阶段:汇编器将hello.s翻译成机器语言指令,把指令打包成一种叫做可重定位目标程序的格式,并将结果保存在目标文件hello.o中。hello.o文件是二进制文件。

1.2.4 链接器

链接阶段:链接器负责将目标文件和外部符号进行连接,得到一个可执行二进制文件,可以被加载到内存中,由系统执行。例如hello程序调用了printf函数,它是每个C编译器都提供的标准C库中的一个函数。printf函数存在于一个名为printf.o的单独的预编译好的目标文件中,这个文件必须以某种方式合并到hello程序中。

2 GCC

  1. GCC是C语言的编译器
  2. 谓编译器,可以简单地将其理解为“翻译器”。实际上,计算机只认识二进制指令(仅有 0 和 1 组成的指令),我们日常编写的 C 语言代码、C++ 代码、Java 代码等,计算机根本无法识别,只有将程序中的每条语句翻译成对应的二进制指令,计算机才能执行。
  3. 调用 gcc 相关指令,将我们编写的程序编译成一个二进制可执行文件。

3 编译过程演示

以hello.c程序为源代码进行分步编译,利用GCC 编译器逐步对源代码进行预处理、编译、汇编以及链接操作。

3.1 预处理操作

预处理,主要是处理那些源文件和头文件中以 # 开头的命令(比如 #include、#define、#ifdef 等),并删除程序中所有的注释 // 和 /* ... */

执行指令 gcc -E ,生成.i 文件,完成预处理操作。

gcc 指令添加 -E 选项,即可控制 GCC 编译器仅对源代码做预处理操作。
gcc 指令添加 -o 选项,将预处理结果写入到文件hello.i中。

[root@VM-8-8-centos data]# gcc -E -o hello.i hello.c
[root@VM-8-8-centos data]# ls
hello.c  hello.i

预处理结果较长,这里先不展开分析。

3.2 汇编操作

执行指令:gcc -S ,GCC 编译器将指定文件加工至编译阶段,并生成对应的汇编代码 .s 文件,完成编译操作。

[root@VM-8-8-centos data]# gcc -S hello.i
[root@VM-8-8-centos data]# ls
hello.c  hello.i  hello.s

得到的汇编代码如下:

注意:所有以"."开头的行为都是指导编译器和链接器工作的伪指令,通常可以忽略这些行。
因此可以简化为以下版本,并添加注释说明(后续补上)

    movq	%rsp, %rbp
    subq	$16, %rsp
    movl	$1, -4(%rbp)
    movl	$2, -8(%rbp)
    movl	-8(%rbp), %eax
    movl	-4(%rbp), %edx
    addl	%edx, %eax
    movl	%eax, -12(%rbp)
    movl	$.LC0, %edi
    call	puts
    movl	-12(%rbp), %eax
    leave

3.3 编译操作

执行指令:gcc -c , GCC 编译器将指定文件加工至汇编阶段,并生成相应的可重定位的 .o 目标文件,完成编译操作。

[root@VM-8-8-centos data]# gcc -c hello.s
[root@VM-8-8-centos data]# ls
hello.c  hello.i  hello.o  hello.s

编译后变成可执行的二进制代码,可以用其他编辑工具将其打开。

3.4 链接操作

执行指令 gcc hello.o 完成链接操作,生成一个可执行的.out文件。默认生成的文件名为的a.out,可以通过 - o 选项指定文件的名称

[root@VM-8-8-centos data]# gcc hello.o -o hello.out
[root@VM-8-8-centos data]# ls
hello.c  hello.i  hello.o  hello.out  hello.s
[root@VM-8-8-centos data]# 

.out文件是可执行文件。使用命令: ./hello.out 。执行结果是在屏幕上输出 hello c

[root@VM-8-8-centos data]# ./hello.out
hello c

4 关于GCC指令

上述例子采用分步操作,也可以忽略中间过程,一般我们都是使用 gcc -c 选项,令编译器将指定文件加工至汇编阶段。

  1. 如果指定文件为源程序文件(例如 hello.c),则 gcc -c 指令会对 hello.c 文件执行预处理、编译以及汇编这 3 步操作;
  2. 如果指定文件为刚刚经过预处理后的文件(例如 hello.i),则 gcc -c 指令对 hello.i 文件执行编译和汇编这 2 步操作;
  3. 如果指定文件为刚刚经过编译后的文件(例如 hello.s),则 gcc -c 指令只对 hello.s 文件执行汇编这 1 步操作。

gcc指令解析:

posted @ 2022-09-06 18:12  拿了桔子跑-范德依彪  阅读(450)  评论(0编辑  收藏  举报