目录
1.Linux编译器-gcc/g++使用
gcc只能用来编译C语言,g++又能编译C又能编译C++
两者使用是相同的,所以统一用gcc来说明。
1.背景知识
1. 预处理(进行宏替换/去注释/条件编译/头文件展开等)
2. 编译(生成汇编)
3. 汇编(生成机器可识别代码)
4. 链接(生成可执行文件或库文件)
2.gcc编译选项
格式 gcc [选项] 要编译的文件 [选项] [目标文件]
我们先来简单体验一下 编译 C 代码并生成可执行文件
我们现在有一个code.c的文件

gcc code.c 这样就生成了一个可执行程序
./a.out 运行

或者gcc code.c -o mycode(mycode可以随便取名)

或者 gcc -o mycode.exe code.c

3.分段
[user1@iZ5waahoxw3q2bZ 26-4-14]$ ll
total 4
-rw-rw-r-- 1 user1 user1 74 Apr 14 18:23 code.c
1.预处理(进行宏替换)
-E:开始进行程序编译,在预处理完的时候,就停下来
• 预处理功能主要包括宏定义,文件包含,条件编译,去注释等。
• 预处理指令是以#号开头的代码行。
• 实例: gcc –E hello.c –o hello.i
• 选项“-E”,该选项的作用是让 gcc 在预处理结束后停止编译过程。
• 选项“-o”是指目标文件,“.i”文件为已经过预处理的C原始程序。
[user1@iZ5waahoxw3q2bZ 26-4-14]$ gcc -E code.c -o code.i //用code.c生成code.i
[user1@iZ5waahoxw3q2bZ 26-4-14]$ ll
total 24
-rw-rw-r-- 1 user1 user1 74 Apr 14 18:23 code.c
-rw-rw-r-- 1 user1 user1 16873 Apr 14 18:33 code.i
可以看到我们的code.c源文件代码就7行,但是预处理之后,整个源文件有了几百行

为什么会有这么多行呢?
根本来说是因为头文件展开
进行宏替换/去注释/条件编译/头文件展开等
如何理解条件编译?
聊一聊编译器?发展
2.编译(生成汇编)
-S:开始编译,编译完了,就停下来
• 在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作, 在检查无误后,gcc 把代码翻译成汇编语言。
• 用户可以使用“-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。
• 实例: gcc –S hello.i –o hello.s
[user1@iZ5waahoxw3q2bZ 26-4-14]$ gcc -S code.i -o code.s
[user1@iZ5waahoxw3q2bZ 26-4-14]$ ll
total 28
-rw-rw-r-- 1 user1 user1 74 Apr 14 18:23 code.c
-rw-rw-r-- 1 user1 user1 16873 Apr 14 18:33 code.i
-rw-rw-r-- 1 user1 user1 448 Apr 14 18:34 code.s
形成汇编语言
为什么C/C++编译,要先完成汇编?
3.汇编(生成机器可识别代码)
-c:开始编译,汇编完成,就停下来
• 汇编阶段是把编译阶段生成的“.s”文件转成目标文件
• 读者在此可使用选项“-c”就可看到汇编代码已转化为“.o”的二进制目标代码了
• 实例: gcc –c hello.s –o hello.o
[user1@iZ5waahoxw3q2bZ 26-4-14]$ gcc -c code.s -o code.o
[user1@iZ5waahoxw3q2bZ 26-4-14]$ ll
total 32
-rw-rw-r-- 1 user1 user1 74 Apr 14 18:23 code.c
-rw-rw-r-- 1 user1 user1 16873 Apr 14 18:33 code.i
-rw-rw-r-- 1 user1 user1 1496 Apr 14 18:35 code.o
-rw-rw-r-- 1 user1 user1 448 Apr 14 18:34 code.s
乱码

code.o---可重定位目标文件,win,vs2022, xxx.obj
已经是二进制的文件了
我们的源文件中,会包含很多的库方法,需要经过链接才能可执行
4.链接(生成可执行文件或库文件)
-o:开始链接,链接完成,就停下来
• 在成功编译之后,就进入了链接阶段。 • 实例: gcc hello.o –o hello
[user1@iZ5waahoxw3q2bZ 26-4-14]$ gcc code.o -o code
[user1@iZ5waahoxw3q2bZ 26-4-14]$ ll
total 44
-rwxrwxr-x 1 user1 user1 8440 Apr 14 18:35 code
-rw-rw-r-- 1 user1 user1 74 Apr 14 18:23 code.c
-rw-rw-r-- 1 user1 user1 16873 Apr 14 18:33 code.i
-rw-rw-r-- 1 user1 user1 1496 Apr 14 18:35 code.o
-rw-rw-r-- 1 user1 user1 448 Apr 14 18:34 code.s
巧记
程序的翻译过程ESc
文件iso
如果只带-c,不带任何其他的。那么默认是生成同名的结构。
[user1@iZ5waahoxw3q2bZ 26-4-14]$ touch code1.c code2.c
[user1@iZ5waahoxw3q2bZ 26-4-14]$ ll
total 4
-rw-rw-r-- 1 user1 user1 0 Apr 14 18:52 code1.c
-rw-rw-r-- 1 user1 user1 0 Apr 14 18:52 code2.c
-rw-rw-r-- 1 user1 user1 74 Apr 14 18:23 code.c
[user1@iZ5waahoxw3q2bZ 26-4-14]$ gcc -c code.c
[user1@iZ5waahoxw3q2bZ 26-4-14]$ ll
total 8
-rw-rw-r-- 1 user1 user1 0 Apr 14 18:52 code1.c
-rw-rw-r-- 1 user1 user1 0 Apr 14 18:52 code2.c
-rw-rw-r-- 1 user1 user1 74 Apr 14 18:23 code.c
-rw-rw-r-- 1 user1 user1 1496 Apr 14 18:52 code.o
一般喜欢先变成.o,然后再通过gcc进行链接
为什么呢?
1.编译器在编译时不仅仅要形成可执行程序,有可能还要形成库。形成库其实就是把所有.o文件打个包。如果要形成库就不用形成可执行程序,就必须形成.o,最后形成对应操作。
2.目前使用vs编译时形成一个可执行程序,自动化代码可能形成很多程序,所以要把.o自由组合,一次性形成多个可执行程序。
4.动态链接和静态链接
• 静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运 行时也就不再需要库文件了。其后缀名一般为“.a”
• 动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由 运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”,如前面所述的 libc.so.6 就是动态库。gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文 件,如下所示。 gcc hello.o –o hello
• gcc默认生成的二进制程序,是动态链接的,这点可以通过 file 命令验证。
ldd code 命令用于查看可执行文件的动态库依赖关系

c标准库,libc.so![]()
库:
分两类:
1.动态库: Linux(.so) windows(.dll)
2.静态库: Linux(.a) windows(.lib)
Linux中命令依赖c标准库
重新改code.c

把#define M注释

-D是 GCC(以及其他许多编译器)的一个编译选项,用于在命令行中定义宏,效果等同于在源代码中使用#define。

如何理解条件编译?
gcc code.c -o code -DM
gcc支持命令行级别的宏定义 -D
gcc code.c -o code -DM=100
#define M 100->插入到我们的代码中
预处理的本质就是修改编辑我们的文本代码
条件编译的用途?
1.对软件进行专业度,收费情况进行区分(业务)。使用条件编译,可以进行代码动态裁剪
2.内核源代码也是采用条件编译进行代码裁剪
3.开发工具,应用软件
为什么C/C++编译,要先完成汇编?
开关打孔编程 二进制
汇编语言
![]()
助记符
C语言 编译器
C++/java/go/python...
C语言用编译器变成汇编然后变成二进制
编译器的语言自举过程:
编译器就产生了---那么编译器在最开始用什么写呢?---第一版二进制版汇编编译器---编译汇编语言了---之后版再用汇编语言,写一个汇编编译器
在这里涉及到一个重要的概念: 库
• 我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该 函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢?
• 最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定 时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样 就能实现函数“printf”了,而这也就是链接的作用
所谓库就是一套方法或者数据集,为我们开发提供最基本的保证(基本接口,功能,加速我们二次开发)
什么叫做动静态库,什么叫做动静态链接,如何理解?
库:
1.动态库: Linux(.so) windows(.dll)
2.静态库: Linux(.a) windows(.lib)
c语言:
libc.so libc.a
Linux中关于库的命名去掉lib前缀,去掉点后面的后缀,就是这个库的真实的名字
动态库内部实现的方法 让我们自己写的程序能在库中找到方法
我们自己的程序,会使用库中的方法 链接起来------形成可执行程序
执行目标方法,需要跳转到库中执行,完了再返回
动态链接,让我们的程序,能找到库中的方法的地址! 共享库--被多个程序共享,一旦缺失,会导致所有程序,无法执行!
静态链接,把我们程序中使用的库方法,拷贝给自己! 静态库只有在链接的时候有用,一旦执行可执行程序,静态库可以不再需要
动态链接是什么时候链的?
动态链接的“链接”动作发生在程序加载时或运行时,而不是编译/链接阶段
程序加载时链接(最常见):当操作系统加载可执行文件时,动态链接器(如 Linux 的
ld-linux.so)会同时加载其依赖的共享库(.so),解析符号并将库中函数的地址填入程序的内存映像中。这个过程在main函数执行之前完成。运行时链接(显式调用):程序运行时可以通过
dlopen()、LoadLibrary()等 API 主动加载动态库,并用dlsym()、GetProcAddress()获取函数地址。此时链接发生在程序运行期间的任意时刻。动态链接在编译时只做符号标记,真正的地址绑定发生在加载/运行时
静态链接是什么时候链的?
静态链接发生在编译/链接阶段
动静态库对比:
1.动态库形成的可执行程序体积一定很小,
2.可执行程序对静态库的依赖度小,动态库不能缺失
3.程序运行,需要加载内存,静态链接的,会在内存中出现大量的重复代码
4.动态链接,比较节省内存和磁盘资源
gcc默认动态链接
那么如果静态链接呢?
静态链接,前提要有C静态库
显示缺失C库
安装 glibc 的静态库版本
sudo yum install -y glibc-static
为了便于区分把名字命名成code-static
可以很明显地看到动静态的大小相差巨大,动态小,静态大
不是一个动态可执行
如果是C++呢?
先安装g++
sudo yum install gcc-c++ -y
找不到静态库
安装C++静态库
sudo yum install libstdc++-static
动态库的本质:
是把语言层面公共的代码,在内存中只出现一次
sudo
再提一遍
![]()
普通用户
![]()
无权限

su -切换root用户进入/etc/sudoers

如果去掉这个user1,普通用户有些权限用不了。所以给重新加进来
技术上理解一下库
5.gcc其他常用选项 - 了解即可
• -E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
• -S 编译到汇编语言不进行汇编和链接
• -c 编译到目标代码
• -o 文件输出到文件
• -static 此选项对生成的文件采用静态链接
• -g 生成调试信息。GNU 调试器可利用该信息。
• -shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
• -O0
• -O1
• -O2
• -O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
• -w 不生成任何警告信息。
• -Wall 生成所有警告信息。
















984

被折叠的 条评论
为什么被折叠?



