【Linux编译器】gcc/g++使用及其原理

【Linux编译器】gcc/g++使用及其原理_第1张图片

目录

一、准备操作

二、gcc/g++编译过程

1、预处理(进行宏替换)

2、编译(生成汇编)

​3、汇编(生成机器可识别代码)

4、链接(生成可执行文件或库文件)

三、函数库

1、静态库和动态库

2、静态链接和动态链接


一、准备操作

程序翻译的过程就是将文本的C语言翻译成计算机二进制。

开始学习之前,我们需要保证自己的服务器上有对应的gcc和g++编译器。

gcc只能编译C语言,g++可以编译C语言和C++。更推荐用gcc编译C语言,用g++编译c++。

1、查看gcc版本: gcc -v

【Linux编译器】gcc/g++使用及其原理_第2张图片

2、查看g++版本:g++ -v

如果发现自己没有g++,可以输入:sudo yum install -y gcc-c++ 安装上去

【Linux编译器】gcc/g++使用及其原理_第3张图片

二、gcc/g++编译过程

1、预处理(进行宏替换)

预处理功能主要包括宏定义,文件包含,条件编译,去注释等。

预处理指令是以#号开头的代码行。

实例: gcc –E hello.c –o hello.i

选项“-E”,该选项的作用是让 gcc 在预处理结束后停止编译过程。

选项“-o”是指目标文件,“.i”文件为已经过预处理的C原始程序。

touch 一个 test.c,并在里面写入代码。gcc 可以自动编译形成可执行程序,也可以形成规定的可执行程序。

【Linux编译器】gcc/g++使用及其原理_第4张图片【Linux编译器】gcc/g++使用及其原理_第5张图片

【Linux编译器】gcc/g++使用及其原理_第6张图片

发现gcc一步把我们的文本变成了可执行程序,但是我们希望可以观察它的预处理过程。 

# - E:从现在开始进行程序的翻译,如果预处理完成就停下来。

① gcc -E 需要编译的文件:

会在屏幕上打印预处理的结果。(不推荐,因为预处理过程也会展开头文件,会占用很大的空间)

【Linux编译器】gcc/g++使用及其原理_第7张图片

② gcc -E 需要编译的文件 -o 生成文件名

把预处理的结果放进新生成的文件里。

【Linux编译器】gcc/g++使用及其原理_第8张图片

2、编译(生成汇编)

在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查 无误后,gcc 把代码翻译成汇编语言。

用户可以使用“-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。

实例: gcc –S hello.i –o hello.s

# -S:从现在开始进行程序的翻译,如果编译完成就停下来

【Linux编译器】gcc/g++使用及其原理_第9张图片3、汇编(生成机器可识别代码)

汇编阶段是把编译阶段生成的“.s”文件转成目标文件

读者在此可使用选项“-c”就可看到汇编代码已转化为“.o”的二进制目标代码了

实例: gcc –c hello.s –o hello.o

# -c :从现在开始进行程序的翻译,如果汇编完成,就停下来

【Linux编译器】gcc/g++使用及其原理_第10张图片

4、链接(生成可执行文件或库文件)

在成功编译之后,就进入了链接阶段。

实例: gcc hello.o –o hello

我们在编译一个可执行文件的过程中,首先会得到一些.o文件,这些.o文件都属于可重定向文件,他们彼此之间是相互独立的,比如test.o文件中虽然引用了printf()函数,但是在.o文件中,printf()函数只是一个未定义的外部符号,test.o也不知道printf()实际的定义在哪。为了得到一个能够直接运行的可执行文件,就需要链接器ld把这些独立的.o文件组装起来,这个组装的过程就是“链接”。

要想执行,就可以直接对二进制文件进行链接,编译器可以自动找到你要使用的库。

链接的过程是有两种方式的:

a、动态链接——需要动态库

b、静态链接——需要静态库

Linux: .so(动态库) .a(静态库)

Windows: .dll(动态库) .lib(静态库)

【Linux编译器】gcc/g++使用及其原理_第11张图片

三、函数库

我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢?

【答案】:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到 系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用。

1、静态库和动态库

  • 静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也 就不再需要库文件了。其后缀名一般为“.a”
  • 动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”,如前面所述的 libc.so.6 就是动态 库。
  • gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件,可以通过ldd 查询是静态库还是动态库,file查询是静态链接还是动态链接。

2、静态链接和动态链接

1)静态链接:将库中方法的实现,真的拷贝到可执行程序中。

静态链接链接库时是以目标文件为单位的,比如我们引入静态库的printf()函数,那么静态链接就会把含有printf()函数的文件链接过来,但因为一个文件里面会放很多不同的函数,这样就会链接了很多此次用不着的函数。静态链接必须使用.a静态库文件

2)动态链接:将库中我需要的方法的地址,填入我的可执行程序中,建立关联。 

动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。动态链接必须使用.so动态库文件。

gcc/g++默认形成的可执行程序是动态链接的。-static 使用静态链接的方式形成可执行程序。

【Linux编译器】gcc/g++使用及其原理_第12张图片

链接失败可能是因为机器上没有静态库

下载C++的静态库 sudo yum intsall -y libstdc++-static

下载C的静态库 sudo yum install -y glibc-static

你可能感兴趣的:(Linux,linux,运维,服务器)