深度解析Linux中编译器gcc/g++

gcc/g++的选项是完全一样的,只不过gcc只能用于编译C语言,g++既能用于编译C语言也能编译C++。本文章统一使用gcc。

程序翻译经历的四个过程

在Linux中想要编译只需要gcc [文件],然后默认 就会形成一个叫a.out的可执行文件,然后再./a.out就可以将文件里的代码跑起来了。

如果想要指定名称直接编译形成可执行文件:

  • gcc [源文件] -o [可执行文件](推荐)
  • gcc -o [可执行文件].exe [源文件]

1 、预处理(进行宏替换/去注释/条件编译/头文件展开等)

bash 复制代码
gcc -E code.c -o code.i

这个-E选项指的是:开始进行程序翻译,在预处理完成时就停下来。

-o后面加个code.i是因为想把预处理的过程写在叫做code.i的临时文件里,否则过程会直接写在显示器上

打开code.ccode.i文件进行对比一下:

可以看到,源文件也就21行,完成预处理后形成的code.i文件居然有八百多行

其实这个现象的根本原因是因为头文件展开了,才导致文件变大了

所谓头文件展开其实就是在预处理时直接将所包含的头文件内容全部拷贝到所形成的目标文件里

根据对比图可以看到预处理过程还包括:宏替换/去注释/条件编译等。

2、 编译(生成汇编)

bash 复制代码
gcc -S code.i -o code.s

这个-S选项指的是:开始将code.i文件生成一个汇编语言文件,编译结束就停下

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

bash 复制代码
gcc -c code.s -o code.o

这个-c选项指的是:开始将code.s文件生成一个叫可重定位目标文件的code.o文件,汇编完成就停下来

形成的这个.o文件此时已经是二进制的文件了

这时的.o文件还不能直接执行,因为该文件只是被编译了,但是我们的源文件中会包含很多的库方法

这时程序还没有与库方法关联起来,所以还不能运行,还需要经过最后一步:链接

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

bash 复制代码
gcc code.o -o code

目前代码里没有使用任何的第三方库,用的都是C语言的标准库和方法,所以不用指明特定的链接选项

上面显性地将四个过程(i、s、o)给展示出来了,但实际上gcc在正常编译时,这种.i .s .o文件一般不会以文件的形式写在磁盘上,一般在gcc编译器启动时在编译器内部,在内存中就处理好了,最终形成可执行文件。

其实在日常编译时,习惯上并不会一个个生成.i .s .o临时文件

一般喜欢将文件直接生成.o文件再打个包生成可执行文件。

动态库与静态库

库是一套方法或者数据集,为我们的开发提供最基本的保证(基本接口、功能),加速我们二次开发。

静态库

  • 特点:
    • 静态库在编译时会直接将方法嵌入可执行文件中,生成的二进制文件是独立的
    • 其文件后缀通常为.a

一般我们的Linux机器没有安装C/C++静态库,安装方法:

bash 复制代码
yum install glibc-static libstdc++-static -y

动态库

  • 特点:
    • 动态库在运行时被加载,可执行文件依赖于动态库
    • 动态库更节省存储空间,且支持共享
    • 其文件后缀通常为.so

动静态库对比:

对比项 静态库 动态库
文件大小 可执行文件较大,库内容嵌入其中 可执行文件较小,运行时需要库文件支持
运行效率 加载速度更快,无需查找库文件 运行时加载,可能略慢
共享能力 不支持共享,每个程序都包含独立的副本 支持共享,多个程序可以使用同一个库
版本控制 更新库需要重新编译程序 更新库无需重新编译,但需注意兼容性

动态链接和静态链接

静态链接:

在我们的实际开发中,不可能将所有代码放在一个源文件中

所以会出现多个源文件,而且多个源文件件之间不是独立的,会存在多种依赖关系

如一个源文件可能要调用另一个源文件中定义的函数

但是每个源文件都是独立编译的,即每个.c文件会形成⼀个.o文件

为了满足前面说的依赖关系则需要将这些源文件产⽣的目标文件进行链接,从而形成一个可以执行的程序。这个链接的过程就是静态链接。静态链接的缺点很明显:

  • 浪费空间:因为每个可执行程序中对所有需要的目标文件都要有一份副本,所以如果多个程序对同一个目标文件都有依赖,如多个程序中都调用了printf()函数,则这多个程序中都含有printf.o,所以同一个目标文件都在内存存在多个副本;
  • 更新比较困难:因为每当库函数的代码修改了,这个时候就需要重新进行编译链接形成可执行程序。但是静态链接的优点就是,在可执行程序中已经具备了所有执行程序所需要的任何东西,在执行的时候运行速度快。

程序运行时是需要加载到内存的,静态链接会导致在内存中出现大量重复的代码,是对内存资源的一种浪费。

动态链接:

动态链接的出现解决了静态链接中的问题

其基本思想是把程序按照模块拆分成各个相对独立部分

在程序运行时才将它们链接在⼀起形成⼀个完整的程序

而不是像静态链接⼀样把所有程序模块都链接成一个单独的可执行文件。

动态链接不像静态链接一样,在调用库方法时直接将所使用的方法直接拷贝一份到我们的源代码中,而是通过将调用的函数改为其在库中的地址,让程序运行时直接在库里完成调用,大大降低了内存的消耗。

动态链接其实远比静态链接要常用得多。比如我们查看下 hello 这个可执行程序依赖的动态库,会发现它就用到了一个c动态链接库:

python 复制代码
$ ldd hello
    		linux-vdso.so.1 => (0x00007fffeb1ab000)
			libc.so.6 => /lib64/libc.so.6 (0x00007ff776af5000)
			/lib64/ld-linux-x86-64.so.2 (0x00007ff776ec3000)
# ldd命令⽤于打印程序或者库⽂件所依赖的共享库列表。

链接时系统默认链接动态库,若要链接静态库需要如下操作:

bash 复制代码
gcc code.c -o code -static

对比动静态库链接:

gcc其他常用选项

• -E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面

• -S 编译到汇编语言不进行汇编和链接

• -c 编译到目标代码

• -o 文件输出到文件

• -static 此选项对生成的文件采用静态链接

• -g 生成调试信息。GNU 调试器可利用该信息。

• -shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库

• -O0

• -O1

• -O2

• -O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高

• -w 不生成任何警告信息。

• -Wall生成所有警告信息。

相关推荐
Y unes2 小时前
《uboot基础命令记录①》
linux·驱动开发·嵌入式硬件·mcu·ubuntu·uboot
姚青&2 小时前
二.文件处理命令-文件操作
linux
陌路202 小时前
简写网络库(2)--封装socket类
linux·服务器·网络
JiMoKuangXiangQu2 小时前
Linux 内存案例:DDR 访问出错?
linux·内存·ddr ecc
Xの哲學2 小时前
从硬中断到 softirq:Linux 软中断机制的全景解剖
linux·服务器·网络·算法·边缘计算
晴天¥3 小时前
Oracle中的概要文件
运维·数据库·oracle
杭州泽沃电子科技有限公司3 小时前
变压器安全,在线监测如何实现?
运维·在线监测·智能运维
lsp84ch803 小时前
MacBookPro运行飞牛Nas,解决合盖亮屏
linux·网络·macbook·nas·飞牛
0思必得03 小时前
[Web自动化] Requests模块基本使用
运维·前端·python·自动化·html·web自动化