Linux编译器 — gcc/g++ 编译器的使用
Linux编译器 — gcc/g++ 编译器的使用
一、背景知识
- 预处理(进行宏替换)
- 编译(生成 汇编)
- 汇编(生成 机器可识别代码)
- 连接(生成可执行文件或库文件)
二、gcc如何完成:gcc/g++ [选项] 要编译的文件 [选项] [目标文件]
格式 gcc/g++ [选项] 要编译的文件 [选项] [目标文件]
gcc与g++一样,都能进行编译,选项都一样
C语言 — gcc(推荐)/g++ ; C++语言 — g++ 。
语言的进化与产生 [ 语言与编译器 自举 的过程 ]
- 使用二进制的这种编程方式,效率太低了。
- 于是就改进了二进制的编程方式,产生了汇编语言 — 不能在计算机上直接执行的 =》编译器 =》就有了操作系统【大名鼎鼎的Unix第一版就是用汇编语言写的】
- 汇编语言用起来还是不太好使 =》C语言 诞生了
- C语言用久了,发现面向对象用的比较多 =》(面向对象编程)C++,(解释型/半解释型语言)【 解释器都是由C语言写的 】Python,shell,Java【Java的虚拟机都是用C语言写的】【C生万物】
用底层语言 写上层语言的编译器 =》
(形成软件)即可将上层语言进行编译 =》
再用上层语言通过 用底层语言写的编译器(可对上层语言进行编译),编译出 用上层语言写的编译器=》
就有了 用上层语言写的 上层语言编译器:这个过程叫作 语言与编译器的自举
汇编语言 转二进制,这些已经由前人写好了。所以只需把 C编译成汇编语言 即可。【 所以 分阶段的 才有了下面 编译过程中的多过程 】
如下指令中,与 -o
(object 目标文件)紧邻的是 形成的指定的可执行程序(要编译的文件) ,剩下的都可以叫 源文件。
-D
指定的宏,
不同版本,是功能上的问题 =》免费版的少,专业版的功能更丰富,且他们重叠的内容都是一样的
那是 放在一起维护,还是分开维护 =》答案当然是维护一份代码好
=》通过 条件编译,对代码进行动态裁剪,软件的多版本的维护。
(1)预处理(进行宏替换) .i
文件为已经过预处理的C原始程序
预处理功能 主要包括 宏定义,文件包含,条件编译,去注释等。
预处理指令 是 以 #
号开头的代码行 。
#
防止头文件被重复包含,为什么?
通过 条件编译,来 将重复的编译进行 去掉 。
//实例y
gcc –E hello.c –o hello.i // .i文件为 **已经过预处理的C原始程序**。
[ 选项 ]
- 选项
“-E”
,该选项的作用是让 gcc 在预处理结束后 停止编译过程(End)。【从现在开始进行程序的翻译过程,当预处理做完的时候,就停下来】
选项“-o”
是指 目标文件(object
),【 不想写后缀,编译器是会自动形成与源文件同名的后缀 】
“.i”
文件为已经过预处理的C原始程序。
如上指令中,与 -o
(object 目标文件)紧邻的是 形成的指定的可执行程序(要编译的文件) ,剩下的都可以叫 源文件。
(2)编译(生成汇编).s
文件
在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,
在检查无误后,gcc 把 代码 翻译成 汇编语言。
[ 选项 ]
- 用户可以使用
“-S”
选项来进行 查看,该选项 只进行编译而不进行汇编,生成汇编代码,生成对应的code.s 文件
。【 从现在开始进行程序的翻译(生成汇编代码),当编译工作做完,就停下来 】
//实例
// 与 -o 紧邻的是 形成的指定的可执行程序 //剩下的都可以叫源文件
gcc –S hello.i –o hello.s //汇编代码
(3)汇编(生成机器可识别代码)目标文件 “-o”
汇编阶段 是把 编译阶段生成的 “.s”
文件 转成 目标文件 “-o”
读者在此可使用
[ 选项 ]
- 选项
“-c”
就 可看到 汇编代码 已转化为“.o”
的二进制目标代码 了
//实例:
gcc –c hello.s –o hello.o
此时的 形成的Linux下的 .o
二进制文件 相当于 windows下的 .obj
可重定位目标二进制文件,是不可执行程序,因为还差一步 链接的过程
(4)连接(生成可执行文件或库文件)
在成功编译之后,就进入了 链接阶段 。自己编写的代码也被打包成 .o
文件,库文件也是被打包成 .o
文件,最后将他们这两个.o
文件 进行链接。
//实例
gcc hello.o –o hello
三、gcc 编译过程 总结
四、函数库
在这里涉及到一个重要的概念:函数库
我们的C程序中,并没有定义“printf”的函数实现。
且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现 。
那么,是在哪里实现 “printf”函数 的呢?
最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了。
-
在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,C语言有自己的标准库,默认给你链接到上面去了 ,这样就能实现函数“printf”了,而这也就是链接的作用
-
即使.o文件中的可执行程序什么都没有,也要跟C标准库进行链接。因为 Linux默认已经给我们提供了C语言的头文件了。
在 libr64目录 下也 已经把很多的库都安装好了
【 平台要支持开发,就必须要提前在系统中安装 语言的标准头文件+库文件(安装环境很重要的一步) 】
ldd
即可 查到可执行文件它所依赖的库文件
无论是动态库还是静态库,你所依赖的库名称:将lib去掉,还有 .so.
所带的后缀全部去掉
五、链接 — 动静态库的理解
函数库一般分为静态库和动态库两种。
动静态库 本质就是文件。 头文件也是文件 。
(1)静态库 .a
静态库是指 编译链接时,把库文件的代码全部加入拷贝到可执行文件中 ,因此生成的文件比较大,但在运行时也就不再需要库文件了。
其后缀名 一般为 “.a”
(2)动态库 .so
动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。
动态库一般后缀名为 “.so”
,如前面所述的 libc.so.6 就是动态库 。
gcc 在编译时 默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件,如下所示。
gcc hello.o –o hello
gcc默认生成的二进制程序,是 动态链接 的,这点可以通过 file 命令验证。
> 动静态库 链接的理解
六、动静态库 小结
(1)函数库
函数库一般分为静态库和动态库两种。
动静态库 本质就是文件。 头文件也是文件 。
(2)动静态库优缺点
(3)动静态库 链接的理解
六、.dll
(windows的动态库)缺失 问题
被多个使用者共享使用,一旦缺失,所有程序都不可以运行了!!
.dll
(windows的动态库)缺失的可能场景:
-
在链接的时候,病毒对你的代码进行了修改(病毒:对库进行恶意替换),当链接时对应的库文件.dll进行链接时,则可执行程序就可以跑起来了,就可以对你的文件进行任意的修改了 。
-
杀毒扫描的是:系统之中可能隐藏的可执行程序 以及 各种静态库 。可能是在扫描过程中,扫描软件可能本身就存在各种bug,扫描的时候将你的库文件搞没了也是有可能的。
七、gcc选项
-E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
-S 编译到汇编语言不进行汇编和链接
-c 编译到目标代码
-o 文件输出到 文件
-static 此选项对生成的文件采用静态链接
-g 生成调试信息。GNU 调试器可利用该信息。
-shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库.
-O0
-O1
-O2
-O3 编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
-w 不生成任何警告信息。
-Wall 生成所有警告信息。
八、gcc选项记忆
ESc
,-iso
例子