gcc编译过程及常用选项

GUN编译套件

GUN编译套件(GNU Compiler Collection),我们将其简称为gcc .GNU编译器套件包括C、C++、 Objective-C、 Fortran、Java、Ada和Go语言前端,也包括了这些语言的库(如libstdc++,libgcj等。),是一系列编译器的集合,下面,我将以一段c代码为例,讲述gcc编译的过程以及我们常用的选项。

GCC编译过程

预处理

当我们编写好一段程序,使用编译器编译时,系统会首先完成预处理部分:

预处理是读取c源程序,对其中的伪指令(以#开头的指令,也就是宏)和特殊符号进行“替代”处理;经过此处理,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。这个文件的含义同没有经过预处理的源文件是相同的,仍然是C文件。但内容有所不同。 伪指令主要包括以下三个方面:
(1)宏定义指令,如#define Name TokenString,#undef以及编译器内建的一些宏,如__DATE__ , __ FILE __,
__ FUNCTION __等;

(2)条件编译指令,如#ifdef,#ifndef,#else,#elif,#endif等。

(3) 头文件包含指令,如#include "FileName"或者#include 等。

如果想单独使c程序只进行预处理,我们可以使用到 -E 选项来生成一个 .i 文件,下面是一个很简单的代码:

/*********************************************************************************
 *      Copyright:  (C) 2020 Xiao yang IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  hello.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(04/07/2020)
 *         Author:  Lu Xiaoyang <[email protected]>
 *      ChangeLog:  1, Release initial version on "04/07/2020 12:02:13 PM"
 *                 
 ********************************************************************************/

#include <stdio.h>

#define DATE "2020年4月7日"
#define A 10

int main(int argc, char *argv[])
{

    printf("%s:%d %s():Hello World!,c = %d\n",__FILE__,__LINE__,__DATE__,A);
    /* 打印c文件名,此处的行号,日期和 A 的值 */
    
    return 0;
}   

在编写好程序后,我们使用命令:

gcc -E hello.c -o hello.i

可生成一个只经过预处理的 .i 文件,查看这个文件,可以看到:在这里插入图片描述
在vim编辑器下,命令行模式使用 G 回车可以到文件最末尾,可以看到:
在这里插入图片描述
观察代码,我们可以看到,预处理将注释全部删除,将宏定义全部直接使用其本来的值替代,而在main之前又有很多代码,这是因为我们的头文件 stdio 本身有包含了很多头文件,在预处理时,需要将他们全部放到 .i 文件中来,便于下一步的编译。值得注意的是,这一个不走并不会检查程序的合法性,即使程序有误,任然会生成 .i 的文件。

编译,优化

编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。关于优化方面可以参考博客:
程序优化问题

我们可以使用命令:

gcc –S hello.i –o hello.s 或者
 gcc –S hello.c –o hello.s 

查看 hello.s:
在这里插入图片描述
系统已将其转化为汇编代码;

汇编

汇编过程实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。
目标文件由段组成。通常一个目标文件中至少有两个段:
代码段(文本段):该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。
数据段:主要存放程序中要用到的各种常量、全局变量、静态的数据。一般数据段都是可读,可写,可执行的。
使用命令,完成汇编过程:

gcc –c hello.s –o hello.o 

查看hello.o文件,会发现是一些乱码:
在这里插入图片描述
vi / vim 命令行模式使用

%!xxd

查看16进制文件:
在这里插入图片描述

链接

汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。
链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体,也就是可执行程序。
根据开发人员指定的库函数的链接方式的不同,链接处理可分为两种:
(1)静态链接 和 (2) 动态链接
对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用静态链接要优越。在某些情况下动态链接可能带来一些性能上损害。生成最后的可执行文件:

gcc hello.o –o hello

经过这一步,就得到了我们最后的可执行文件。可以使用

./hello

运行程序啦!

总结

下面是我做的一个总结

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_45121946/article/details/105360792