Linux编译器和调试器的使用

大家好!上一篇的Linux文章,我们学习了Linux的编辑器。这篇我们就来学习一下它的编译器和调试器。
在这里插入图片描述

1. Linux编译器-gcc/g++使用

我先带大家来看一下如果使用的:
.c文件的编译:
在这里插入图片描述
.cpp文件的编译:
在这里插入图片描述
还有g++可以编译.c文件和.cpp文件。但是gcc只能编译.c文件,不能编译.cpp文件。
在这里插入图片描述
我们看到gcc编译报错了。

1.1 背景知识

在前面的学习中,我们知道了一个程序(文本)必须转换成机器语言(二进制),这样计算机才能认识。那么为什么计算机只认识二进制呢?原因是:组成计算机的各种组件,只能认识二进制。如果,我们直接运行的是程序语言,那么计算机的各种组件的转换成本太高了。

一个程序到运行,我们知道有四个步骤:
1. 预处理:宏替换,头文件展开,去注释,条件编译。
2. 编译:把C语言翻译成汇编语言。
3. 汇编:把汇编语言翻译成可重定位的二进制文件(.o/.obj)
4. 连接:

下面,我们就说一下如何用gcc来独自完成以上步骤。

1.2. gcc如何完成四步骤

首先,我们vim进入到test.c文件中编写一些代码:
在这里插入图片描述
我们在test.c文件中加上了头文件,宏定义,注释,条件编译这些。现在我们就操作一下:
在这里插入图片描述
在我们默认gcc编译的情况下,文件的名字叫做a.out,现在如果我们想自己定义名字,我们可以用gcc -o这个命令。
在这里插入图片描述

1.2.1 预处理

我们看到程序运行出来没有问题,但是它是一步到位从文本到计算机认识的机器语言。如果我们只想看预处理,我们可以这样:
在这里插入图片描述
这里的-E的意思是:从现在开始给我进行程序的翻译,当预处理完成,就停下来
而后面的-o test.i的意思是:把预处理后生成的内容写到test.i这个临时文件中。如果我们不写,那么就会把内容直接显示到显示屏上。

然后我们进入到test.c文件中
在这里插入图片描述
我们切换到底行模式,然后输入vs 文件名:
在这里插入图片描述
在这里插入图片描述
从上面的对比我们能看到,它的每一步都体现出来了。因为我们没有宏定义DEBUG,所以它打印的是release。如果我们定义了,它就会打印DEBUG。
在这里插入图片描述
现在我们是分屏模式。如果我们想来回切换可以在命令模式下按:Ctrl+ww

有一个问题:就是我们平时写源文件时,里面会#include头文件,而这些头文件在Linux平台的哪里呢?
在这里插入图片描述
这些头文件都是安装在这些目录下。我们以后如果要引入头文件,我们就需要看这里有没有安装,如果没有安装我们还需要自己安装。
平时我们写头文件就直接#include头文件就行了,那么编译器是如何找到这些文件的呢?
原因就是:编译器内部都会通过一定的方式,知道你包含的头文件所在路径

1.2.2 编译

那么,我们想看编译这个过程呢?我们可以这样去做:
在这里插入图片描述
这里的-S的意思是:从现在开始进行程序的翻译,当编译完成之后,就停下来
这里我们可以从test.i开始,也可以从test.c开始。如果从test.c开始就会重新预处理。
在这里插入图片描述
此时就形成了汇编语言了。如果我们想进行第三步:汇编的过程。我们需要这样做:

1.2.3 汇编

在这里插入图片描述
这里的-c的意思是:从现在开始进行程序的翻译,当汇编完成之后,就停下来
在这里插入图片描述
此时就会有一个test.o文件,如果我们打开里面看的话。里面都是一些乱码。
好了,到这里可能有的同学会认为现在的test.o文件可以运行了。但是我想说的是现在的test.o文件还不能运行。
原因是:从开始的第一步到现在我们编译的都是test.c文件的代码,也就是只编译了我们自己的代码。而我们编写的像printf函数,我们没有自己去实现这个函数,它是由C语言提供实现的。现在你的代码和C语言提供的printf函数就会有两个问题:
1.我的代码中需要的printf在哪里?
答案是:在C标准库中。
在这里插入图片描述
在Linux就是这个库。

2.我的代码中使用了printf,如何和C标准库的printf的实现产生关联(链接)?
我们使用gcc不加任何选项时,gcc编译器会默认在我们的系统路径下搜索我们所需要的库。然后链接形成可执行程序。
在这里插入图片描述
如果我们想看这个可执行程序它所依赖了哪些库,我们可以使用命令行的形式
在这里插入图片描述
在这里涉及到一个重要的概念:函数库。

2. 函数库

我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么是在哪里实现“printf”函数的呢?
最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用。

结论:
头文件:给我们提供了可以使用的方法,所有的开发环境,具有语法提示,本质是通过头文件帮我们搜索的。
库文件:给我们提供了可以使用的方法的实现,以供链接,形成我们自己的可执行程序。

2.1 静态库和动态库

函数库一般分为静态库和动态库两种。
动态库在Linux下以.so结尾,在windows下以.dll结尾。
静态库在Linux下以.a结尾,在windows下以.lib结尾。
而我们链接动态库的方式叫做动态链接,链接静态库的方式叫做静态链接。

动态链接:大家共享一个库。
优点:可以节省资源。缺点:一旦库缺失,会导致几乎所有的程序失效。
静态链接:将库中的相关代码,直接拷贝到自己的可执行程序中。
优点:不依赖任何库,程序可以独立执行。缺点:浪费资源

那么现在有一个问题:gcc中是如何来体现的呢
我们用file指令可以查看文件的构成:
在这里插入图片描述
executable:这个意思是可执行的。
dynamically linked:这个叫做动态链接。
所以,gcc中默认情况下,形成的可执行程序就是动态链接。
如果我们想用静态链接来生成一个文件,我们可以这样做:
在这里插入图片描述
但是你会发现我们找不到,原因是:默认情况下,都没有带静态库。我们需要自己手动安装一下:
在这里插入图片描述
这是C语言版本的静态库。
在这里插入图片描述
这是C++版本的静态库。大家可以自己去安装一下。
在这里插入图片描述
安装后,就可以链接静态库。但大家可以看到mytest2的大小是861384个字节。它比链接动态库的生成的文件大100倍。所以我们更加推荐动态链接。

3. Linux调试器-gdb使用

我们创建一个gdb目录,进入这个目录下创建一个test.c文件。
在这里插入图片描述
然后vim进去编写一段代码:
在这里插入图片描述
我们保存退出。然后编译这个test.c文件。
在这里插入图片描述
从这个我们可以看到有错误。这是因为:在循环里定义变量,是一个比较新的标准。我们可以这样写:
在这里插入图片描述
然后我们对它进行运行:
在这里插入图片描述
好了,没有问题。但是如果现在我们想调试它,该怎么调试呢?
在当前的目录下,执行gdb 可执行程序
在这里插入图片描述
我们可以看到出现了这样一句话:no debugging symbols found,没有找到调试符号。
原因是:Linux下默认形成的可执行程序是release版本,无法调试
那么我们该怎么样改成debug版本呢?我们需要加上这个选项:
在这里插入图片描述
加上这个选项就是debug版本,从体积上我们也能看到debug版本的体积比release版本的要大一些。这些就是调试信息。

好了,准备工作已经完成。我们看看到底是如何来调试的:
如果要调试,我们需要看到我们的代码。在Linux下有一个命令叫做list,也可以简写成l
在这里插入图片描述
我们看到它只显示了一小部分,而且是从第9行开始的,如果我们想从第0行开始,我们可以这样:list/l 行号:显示源代码,接着上次的位置往下列,每次列10行
在这里插入图片描述
如果我们想看到main函数开始,我们可以这样:list/l 函数名:列出某个函数的源代码
在这里插入图片描述
如果,我们继续按回车,gdb会执行上一条命令。
在这里插入图片描述
如果我们想退出gdb,我们有这样的命令:quit(q):退出gdb
在这里插入图片描述
在vs下,我们知道调试可以打断点,那么在Linux下我们又如何操作的呢?
break(b) 行号:在某一行设置断点
在这里插入图片描述
b加上你要打断点的行数。如果我们要查看我们所打的断点,我们可以这样:
info break :查看断点信息。
在这里插入图片描述
意思是:第一个断点在这个test.c文件的第18行。我们再加一个断点:
在这里插入图片描述
此时,我们想一步一步调试,我们可以用这个指令:r或run:运行程序
在这里插入图片描述
它会给我们停在第一个断点处。如果我们想查看这个变量的信息,我们可以用这个命令:
p 变量:打印变量值
在这里插入图片描述
如果,我们想看一个变量的地址,我们可以这样:p &变量:打印变量的地址
在这里插入图片描述
如果我们想逐语句调试(在vs下是F10),我们可以这样:n 或 next:单条执行
在这里插入图片描述
我们可以看到遇到函数并没有进去,如果我们想进去函数里面,我们可以这样:s或step:逐语句
在这里插入图片描述
现在有两个断点,我们现在在第一个断点,我们该如何直接从第一个断点跳转到第二个断点呢?
有这样一个命令:continue(或c):从一个断点直接跳转到另外一个断点
在这里插入图片描述
那么打断点我们学会了,如何取消断点呢?
在这里插入图片描述
我们看到并不能直接d多少行。而是应该这样写:
delete(d) n:删除序号为n的断点
在这里插入图片描述
那么还有一个问题:在Linux下如何打开监视窗口呢?
在这里插入图片描述
在这里,我们可以看到虽然循环在不断的进行,但是我们不能看到值的变化。如果我们使用p,它不能一直显示。
在这里插入图片描述
display 变量名:跟踪查看一个变量,每次停下来都显示它的值
在这里插入图片描述
在vs窗口下,如果我们想取消某个变量的监视,我们直接删除就行了。在Linux下如何操作呢?
undisplay n:取消对先前设置的那些变量的跟踪,n为变量前的序号
在这里插入图片描述
在这里,我们再说一个命令:breaktrace(或bt):查看各级函数调用及参数
在这里插入图片描述
那么现在,我们调试的时候想跳出这个循环该怎么办呢?我们有这样的命令:until X行号:跳至X行
在这里插入图片描述
我们将直接跳转到第11行。但实际上到12行了,因为11行没有写代码,所以它来到了第12行。然后我们继续next的话就会从12行执行了。
在这里插入图片描述
或者使用这个命令:finish:执行到当前函数返回,然后停下来等待命令
在这里插入图片描述

总结

在这篇文章中呢,我们初学者主要要记住的是gcc 文件名 -o 文件名这个命令。对函数库,我们现在要感性的理解,这个内容很重要。对于gdb的使用,我们不需要精通它,但一些基本的打断点,运行,查看变量等,我们需要掌握一下。好了,如果大家觉得我的文章有帮助,可以点赞支持。谢谢大家!
在这里插入图片描述

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学代码的咸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值