由源码到可执行文件
我们知道要把我们所写的程序变成可执行文件需要进行4步操作才行,首先需要经过预处理(宏替换),在这个步骤会把引入的头文件进行展开,写的宏也会把它替换好。接着就是对程序进行编译,在这个步骤中会生成一个汇编代码。第三步,就是汇编,在汇编阶段会将编译过程生成的汇编代码生成机器可识别的代码。最后一步,就是链接,如果我们写的代码需要多个源文件,这一步就是将各个源文件连接起来。
预处理
在Linux中,使用gcc -E mycode.c -o mycode.i
,这句指令就是对我们所写的代码进行预处理操作。它的文件后缀是.i
,在这个文件中,我们可以看到它会将我们引入的头文件进行展开。
#include<stdio.h>
#include<stdlib.h>
int main()
{
printf("hello world\n");
return 0;
}
编译
经过预处理后 ,我们得到了一个展开了头文件的.i
文件,接着就是对这个文件进行编译让他生成一个汇编代码。执行gcc -S mycode.i -o mycode.s
命令。在这个阶段,gcc首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,把gcc代码翻译成汇编语言
汇编
紧接着就是对汇编代码进行汇编的工作,也就是将汇编代码生成机器可识别的代码。执行gcc -c mycode.s -o mycode.o
,我们知道,机器能识别的代码是二进制代码,那就不是我们能看懂的了。
链接
最后一步就是对这个二进制文件进行链接操作生成可执行文件,语言也有库,它有一套头文件和一套库文件,需要连接来将代码中的函数调用、外部数据关联起来。执行gcc mycode.o -mycode.out
.
函数库
- 我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的"stdio.h"中也只有该函数的声明,而没有定义函数的实现,系统把这些函数实现都被写到名为libc.so.6的库文件中去了,在没有特别指定时,gcc会到系统默认的搜索路径"/usr/lib"下进行查找。也就是链接到libc.so.6库函数中去,这样就能实现函数" printf"了,而这也就是链接的作用。
静态库和动态库
库函数一般分为静态库和动态库两种。
- 静态库就是编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较打,但在运行时也就不要库文件了。其后缀名一般为".a"
- 动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接加载库,这样可以节省系统的开销。动态库一般后缀名为 “.so”,如前面所述的
libc.so.6
就是动态库。gcc在编译时默认使用动态库。完成了链接之后,gcc就可以生成可执行文件
使用ldd mycode.out
查看链接
gcc选项详解
- -E 只激活预处理,这个不生成文件,需要把它重定向到一个输出文件里面
- -S 编译到汇编语言不进行汇编和链接
- -c 编译到目标代码
- -o 将编译得到的文件输出到指定文件
- -static 此选项对生成的文件采用静态链接
- -g 生成调试信息。GNU调试器可以利用该信息
- -shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统有动态库
- -O0 、-O1、-O2、-O3 编译器的优化选项4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
- -w 不生成任何警告信息
- -Wall生成所有警告信息
Linux调试器-gdb的使用
程序的发布方式有两种,debug模式和release模式,Linux的gcc/g++出来的二进制程序默认是release模式,要使用gdb调试,必须在源代码生成二进制程序的时候加上-g选项
使用方法
前面说到gcc/g++编译器默认生成出来的可执行程序是release模式,而我们如果要使用gdb调试的话就需要生成出来的可执行的程序是debug模式。这就要使用一个参数-g
,以此来生成可以调试的文件。
命令:gcc mycode.c -o mycode -g
我们重新写一下mycode中的代码,以便我们调试
1 #include<stdio.h>
2 #include<stdlib.h>
3
4 int SumToTop(int top)
5 {
6 int sum = 0;
7 for(int i = 0; i <=top; ++i)
8 {
9 sum += i;
10 }
11 return sum;
12 }
13 int main()
14 {
15 int top = 100;
16 int result = SumToTop(top);
17 printf("result = %d\n", result);
18 return 0;
19 }
首先我们先将mycode.c进行编译
通过这编译我们可以得到一个名为mycode的可执行文件。再使用 gdb mycode
就可以进入调试模式。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DGlD3bSQ-1648483402727)(C:\Users\22114\AppData\Roaming\Typora\typora-user-images!在这里插入图片描述
)]
在gdb模式下,输入run
就直接将整个程序运行完,如果想要退出gdb模式就输入quit
指令,就可以退出gdb模式。
- list 或 l 行号:显示程序的源代码,接着上次的位置往下,每次显示10行
- list 或l 函数名 列出某个函数的源代码
- r或run 运行程序
- b或break 行号 在某一行打断点
- info break或info b: 查看断点信息
PS:打好断断点后再使用run指令,程序会执行到断点处停下
- s或step : 逐语句执行会进入调用函数函数
n或next: 逐过程执行,单条语句执行
finish: 执行到当前函数返回,然后停下来等待命令
- delete breakpoints: 删除所有断点
- delete breakpoints n: 删除序号为n的端点
- disable breakpoints:禁用断点,也就是断点还在但是程序执行时不停在断点处
- enable breakpoints:启用断点
- display 变量名:跟踪查看一个变量名,每次停下来都显示它的值
- undisplay:取消对先前设置的对那些变量的追踪
- until x行号: 跳至x行
- breaktrace(或bt):产看各级函数的调用以及参数