gcc
一个代码经过翻译生成可执行程序有以下四个阶段:
1.预处理(宏替换,去掉注释,头文件展开)
2.编译(生成汇编语言)
3.汇编(生成可重定向的二进制语言)
4.链接(调用标准库,查询符号表…)
今天在Linux上,我们继续详谈这四个阶段:
(
1
)
(1)
(1)预处理 -E
格式 gcc -E .c结尾的文件
功能:文件预处理完以后便停止
这句命令会将预处理以后的结果显示到屏幕上,本身就没有多少意义。
因此可以配合-o后缀进行使用,-o的含义是将结果输出到指定文件中。
预处理完的文件以.i结尾。

(
2
)
(2)
(2)编译 -S
格式 gcc -S .i结尾的文件
ps::这里的文件也可以.c结尾,但是这样就得重复执行一次预处理。
功能:文件执行完编译以后便停止
编译完以后生成的文件以.s结尾

前面我们说编译阶段的功能是生成汇编语言,关于这点我们可以进行验证:

(
3
)
(3)
(3)汇编 -c
格式:gcc -c .s文件
功能:文件运行完汇编就停止
汇编生成的文件以.o结尾

汇编生成的文件是可重定向二进制文件,对于这个我们也可以验证。
命令:od .o文件
功能:将文件以8进制或者其他格式显示出来

问大家一个问题:经过汇编以后的文件可以运行吗?
答案:经过汇编以后的源代码虽然是二进制,但是是可重定向的二进制,计算机还是不能直接运行,还需要进行链接。
(
4
)
(4)
(4)链接
链接的命令非常简单
格式:gcc 文件名
链接阶段有一个作用:调用标准库
如果我们执行的语言是C语言,对应在调用的就是C标准库。C语言给我们提供了一系列的头文件和标准库。平常我们执行printf,并不是就执行这个代码,而是调用C标准库中的printf函数,C标准库对printf函数进行了封装。链接阶段我们会调用这个函数,本质上调用的其实是标准库中的printf函数。
总的来说:链接就是将自己写的代码和第三方库链接在一起,关联起来。
动态库和静态库
在Linux中,库又分为两种:
(
1
)
(1)
(1).a结尾的静态库
(
2
)
(2)
(2).so结尾的动态库
类比windows中的动静态库:
(
1
)
(1)
(1).lib结尾的静态库
(
2
)
(2)
(2).dll结尾的动态库
在动态库和静态库的基础上又引申出了静态链接和动态链接。
如何理解动态链接和静态链接?
动态链接:动态链接就是当自己的代码需要用到库的时候去调用库函数的代码。
静态链接:静态链接就是把自己的代码和库中关联部分的代码拷贝到自己的文件中
ps::
动态链接使用的是动态库。
静态链接使用的是静态库。
动态链接的优点和缺点:
优点:调用动态链接生成的文件相对于静态链接小很多,节省内存和硬盘空间。
缺点:如果第三方库没了,动态链接生成的程序就无法正常运行。
静态链接的优点和缺点:
优点:可移植性高,即使第三方库挂掉了,也能够正常运行。
缺点:占用内存和硬盘空间较大
gcc默认使用的是动态链接还是静态链接呢?
通过file命令查看得出gcc默认使用的是动态链接。

如何生成一个静态链接的可执行程序?
在使用静态链接之前,我们需要先安装C语言的静态库
sudo yum install glibc-static
格式 gcc 文件名 -static
功能:生成可执行程序并使用静态链接

对于静态链接和动态链接生成的可执行程序我们可以进行对比:
静态链接生成的可执行程序的大小是动态链接生成的可执行程序的数十倍。
再次说明了静态链接占用的内存和硬盘空间大。

Linux中还可以查看可执行程序依赖的第三方库:
格式:ldd 文件名

g++翻译源文件的过程和gcc基本相同,但是C语言最好使用gcc,C++最好使用g++。
ps::对于Linux中编译,链接,汇编的命令也很好记忆,
对应ESc,也就是我们电脑上的Esc按键(不过S是大写)。
而生成的文件对应就是iso(可以联想到苹果的iso)。
gdb调试
你没看错,在Linux上也可以进行代码的调试。
之前觉得vs 2017调试就很麻烦,现在对比Linux看来,vs 2017简直是天堂= =。
虽然很麻烦,但是咱还是得学!
命令:gdb 可执行程序
功能:进入调试模式
对于gdb的学习我们进行类比vs 2017上的调试
在vs 2017中,翻译生成的可执行程序分为Debug版本和release版本。
Debug版本的可执行程序中加入了debug信息,所以相比release文件来说占用更大。

一个程序如果想要进行调试,那么这个可执行程序就需要含有debug信息,在Linux上也是如此。
所以如果想要通过gdb调试一个可执行程序,那么在生成可执行程序的时候就需要带入debug信息。
在Linux中,生成可执行程序的时候加上后缀-g,生成的可执行文件就会自动被加入debug信息。
并且因为结构要求:生成带有for循环这类文件的可执行程序的时候,还需要带上-std=c99,否则不能正常运行。
ps::使用gdb之前记得安装sudo yum install gdb
格式:gdb 可执行程序
功能:进入gdb模式
进入gdb调试模式以后,需要先让程序跑起来。
命令:run(简写r)
功能:运行程序
(
1
)
(1)
(1)list(查看代码)
格式 list(简写l)
功能:显示可执行程序的源代码,每次会在上一次的基础上进行显示

格式 list(简写l) 函数名
功能:显示该程序的源代码

ps::输入完list以后要是想继续使用list的功能,输入回车就行
(
2
)
(2)
(2)breakpoint(简写b) 打断点
格式:b 数字
功能:在指定行打断点

格式 b 函数名
功能:在指定函数体内部的第一行打上断点

(
3
)
(3)
(3)查看断点
格式: info b
功能:查看当前所有断点的状态

格式 d 断点的编号
功能:删除这个编号的断点
在vs 2017中,还能对断点进行禁用。
在Linux上也行
格式:disable 断点的编号
功能:禁用这个编号的断点

格式:enable 断点的编号
功能:开启这个断点
( 4 ) (4) (4)逐过程和逐语句
ps::逐过程不会进入调用函数内部,逐语句会进入调用函数内部。
格式 step(简写s)
功能:逐语句查看当前的代码,类似vs 2017下F11

格式 next(n)
逐过程查看源代码

(
5
)
(5)
(5)display 查看
格式: display 变量名
功能:显示变量的值

格式: display &变量名
功能:显示变量的地址

这个查看的功能类似vs 2017中的监视功能,并且在Linux中每一个监视变量都有自己的编号

既然能设置监视变量,对应就需要删除监视变量
格式: undisplay 监视变量的编号
功能:删除这个监视变量

(
6
)
(6)
(6)continue和until
格式:contiune
功能:从当前位置继续运行程序
格式:until 数字
功能:在函数内部执行到指定行停止

项目自动化构建工具
对于一些中大型的项目,其背后有非常多的源文件需要进行编译,如果每一个文件都使用gcc的方式进行编译,就会导致工程量巨大,并且容易出现错误。所以Linux上有一种更为简便的工具Makefile
make和Makefile需要搭配进行使用
make是一条命令,makefile是一个文件。
Makefile这个文件中需要维护两种依赖:
1.依赖关系
2.依赖方法
我们举例说明什么是依赖关系和依赖方法:
快到月底了,大学生小明的生活费所剩无几了,这时他就向他老爸打了一个电话:爸,我是你儿子,给我打点生活费。
这里面小明和父亲的父子关系就是一种依赖关系,而打生活费就是依赖方法。
也就是说只有依赖关系和依赖方法共同作用的时候才能达到某种目的/动作。

依赖方法和依赖关系属于一种相辅相成的关系,一方的存在会限制另一方能够作用的范围。
举例来说:你总不可能让你的舍友给你打钱。你和你舍友的依赖关系已经限制了你们之间不能使用这种依赖方法。

格式:make
功能:生成Makefile中的第一个目标文件

格式:make 目标文件
功能:生成Makefile文件中指定的目标文件

需要注意,对于已生成的目标文件,如果源代码没有发生改变,不可以二次make重复生成。

格式:.PHONY 目标文件
功能:被.PHONY修饰以后的目标文件会变成伪目标,伪目标可重复生成目标文件
ps::也就是可以不断的进行make


通过makefile如何同时生成多个可执行程序
定义伪目标all,将all的依赖关系设置为你想要同时生成的程序。
因此在执行make的过程中,需要先为all生成其依赖关系,这时就可以同时生成多个可执行程序,又因为all没有依赖方法,所以不会生成all可执行程序。

本文详细介绍了GCC编译器的工作流程,包括预处理、编译、汇编和链接四个阶段,解析了动态库与静态库的区别及应用,探讨了gdb调试工具的使用方法,并介绍了Makefile在项目自动化构建中的作用。
3003





