执行代码统称为翻译过程,程序翻译过程的基础知识如下:
创建test.c,对应指令解析为:
gcc -E test.c//test.i
-E表示开始翻译程序,完成预处理之后停下来,此时产生的文件为test.i。
这条指令不好用,因为打印的是所有输出结果,不便于观察,所以经常这样写:
gcc -E test.c -o test.i
-o代表指定生成某种文件,同样的,现在需要完成预处理之后停下,那么对应生成的应该是test.i文件。
gcc -S test.i -o test.s//前提是要产生了.i文件
-S表示开始翻译程序,完成编译之后停下来,此时产生的文件为test.s,里面包含程序对应的汇编语言。
为什么需要这一步?因为计算机不可以直接执行汇编语言,需要将汇编语言转化为二进制。
gcc -c test.s -o test.o//前提是要产生了.s文件
此时产生文件为test.o。
和前面同理,不过这里的-c是小写,代表执行完汇编过程就停下,也就是生成机器可识别的代码,其实就是二进制程序,但是此时还是不能被直接执行;
将生成的所有.o文件以及对应的头文件打包起来,就成为了一个库;为什么需要头文件?因为需要提供接口。
gcc test.o
这种方式默认形成的是a.out,也可以通过下列这样形成指定名称的可执行文件
//两种方式一样
gcc test.c -o test
gcc -o test test.c
Linux下生成的可执行程序默认是realase版本的,需要转换成debug版本
执行下列指令
gcc -o test.c test -g
-g的意思是添加调试信息,也就是debug版本。
进到(gdb)命令行,先让程序跑起来:
(gdb)run;//(gdb)r
不断执行list或 l 可以查看到行号,也就可以明确在哪个地方打断点:
(gdb)list//(gdb)l
(gdb)l 1//l n代表从n行开始
还可以通过函数名查看:
(gdb)l main;//查看main函数主体
但是这种方式会带有上部分代码的几行代码,是为了和上部分代码形成关联性。
断点全称breakpoint,简写为b,指定第n行打断点:b n
(gdb)b 5;//第5行打断点
(gdb)info b;//查看断点信息
断点信息里面包括了行号、地址等,还有该断点是否启用。
打完断点以后,再次执行run,可以看到程序跳转至对应断点处。
运行分为逐过程next(简写为n,不进入函数)和逐语句step(简称s,需要进入函数)。
(gdb)next;//(gdb)n
(gdb)step;//(gdb)s
gdb中的监视是display,显示某个值的信息,包括所在行、值、地址等,该指令不能简写。
(gdb)display n;//显示n的数值信息
(gdb)display &n;//显示n的地址信息
每次执行该指令后,再多次执行n或者s指令,可以直接常显示对应值的变化情况,就不用一直调用display了,这个设定也包括了你接下来监视的变量,和vs2019中类似。
若此时想删除变量,可以执行undisplay,但是后面跟的不是某个变量,而是该变量开头对应的代号:
(gdb)undisplay 1;//删掉监视窗口中代号为1的变量
(gdb)undisplay 1 2;//删掉监视窗口中代号为1和2的变量
若只想查看一次:
(gdb)p n;//只查看一次
finish表示直接结束当前函数
(gdb)finish;//直接得到函数返回值
另一种方式叫做continue,表示跳到下一断点:
(gdb)continue;//直接跳到下一断点
还有一种方式until,表示直接运行到某一行:
(gdb)until n;//直接跳到第n行
删除断点delete,也是要按照对应的编号,而不是指定哪个行的断点:
(gdb)d 2//删掉2号断点
禁用断点undisable:禁用后断点状态由y->n
(gdb)disable 2//禁用2号断点
(gdb)enable 2//启用2号断点
有时候程序崩溃比较难精确找到行号,有一种方式是利用core dump标志位。
这个标志位的作用就是:如果程序异常退出,它会被置1,并且生成core文件。
但是这有前提:
首先在云服务器上,Linux中的这个标志位的功能是被关闭的。所以想要打开这个功能,要先向core file size中写入大小,表示打开这个功能,然后core dump标志位才会有上述功能。
打开这个功能:
执行ulimit -a查看系统资源,第一行的core file size是0,说明默认关闭:
我们可以先设置它为任意大小:
ulimit -c 10240
这表示系统允许你使用core dump位,此时我们故意写出一个浮点数错误的程序:
int main()
{
int a = 0;
a /= 0;
return 0;
}
运行结果可以看出直接打印了浮点数错误,并且还增加了一个core.1678文件:
每次产生的core文件会不一样,也就是每次执行可执行文件,OS给你的调试的信息不一样,然后就可以进行gdb调试:
可以清楚地看见,由于出错收到的信号、出错的行号、出错的代码都已经被找出来并打印到了屏幕,这种方式叫做事后调试。