
💭 写在前面:本文将介绍如何使用 GCC 编译器编译,并详细介绍了 Makefile 的基本构造、创建Makefile 文件以及 Makefile 变量,以提高编译效率。此外,本文还将探讨GDB调试器的使用,包括调试前的准备、readelf 读取 ELF 文件信息、显示代码、断点、调试、监视、跳转等内容。
📜 本章目录:
0x03 显示代码 gcb(list) (List file)
编译顺序:C 预处理阶段 → C编译 → 汇编 (Assemble) → 链接 (Linking)
gcc,即 GNU Compiler Collection,是一个编译器套件。
gcc 不仅可以编译 C 语言,还可以编译其他语言。
在 C 语言中生成可执行文件的过程如下:
gcc 通过调用这些编译器和链接器来生成可执行文件。
输入 gcc -v 可以确认 gcc 版本:
$ gcc -v

❓ 那么如何使用 gcc 呢?很简单:
$ ./a.out
如果想要创建其他名称的可执行文件,则可以使用:
gcc -o 可执行文件名 源文件名.c
gcc 源文件名.c -o 可执行文件
💭 例如:gcc -o mytest test.c,注意,-o 后面必须跟输出的文件名!

* 我们可以通过 man 手册去查询 gcc 更多的信息:
$ man gcc

-v 选项:可以显示编译过程
$ gcc -v 源文件名.c
可以查看当前使用的 gcc 版本以及插入到源代码中的 stdio.h 所在的目录等信息。gcc编译器版本为 5.4.0,gcc 访问头文件(如stdio.h)的路径为 /usr/lib/gcc/x86 64-linux-gnu/5/include 等。

-D 选项:在外部定义预处理器宏
$ gcc -D test.c
-c 选项:仅编译选项(当需要编译多个源文件时需要)
$ gcc -c test.c

有以下有两种方法:
1. gcc –o main main.c fun1.c fun2.c
2. gcc –c main.c
gcc –c fun1.c
gcc –c fun2.c
gcc –o main main.o fun1.o fun2.o
虽然第二种方法看起来更麻烦,但是当fun1的源代码被修改时,通过使用目标文件,我们可以只重新编译已更改的文件,然后将其与现有文件链接,而不进行内部低效的操作。
-I 选项:指定 #include 语句中头文件所在的目录。用于链接库文件的
如果使用 #include <stdio.h>,则以系统标准目录 /usr/include 为基础查找文件并包含它。 如果使用 #include "stdio.h",则以当前正在执行编译器的目录为基础查找头文件并包含它。 如果不是这两个目录,则明确指定为 -I<目录>。
❓ 我们为什么需要 Makefile?举个最简单的例子:
如果需要跑的工程,源文件有十几个甚至上百个呢?难不成我们要:
gcc –c main.c
gcc –c fun1.c
gcc –c fun2.c
…
gcc –c fun100.c
gcc –o program main.o fun1.o fun2.o … fun100.o
键盘寿命疯狂 -1,CV C 到冒烟?不用担心!Makefile 解君愁!
target ... : prerequisites ...
recipe
....
target (目标):文件的名称,如可执行文件,目标文件等等。最先出现的目标文件称为默认目标(通常是可执行文件) ,目标文件是 make 的最终目标。
prerequisites (先决条件):为了生成目标文件所需的材料。
recipe (配方):Make 执行的操作。当先决条件发生变化时,按照规则生成目标文件。只有需要更新的目标才会进行更新。
* 如果先决条件发生变化 按照规则生成目标文件 (只有需要更新的目标才会进行更新)
Makefile 是用于在 Linux 系统中简化重复编译过程的配置文件。
通过 Makefile 可以管理库和编译环境,我们推荐将文件命名为 Makefile。
CC = gcc
target1 : dependency1 dependency2
command1
command2
target2 : dependency3 dependency4 dependency5
command3
command4
* 注意:command 前必须要加一个 tab!
什么是 Makefile 中的宏定义?在 Makefile 中一个预定义的环境变量。
一个 Makefile 由 目标 (target) 、依赖关系 (Dependency) 和 命令 (command) 组成。
Makefile 和一般的编译过程的区别在于:
Makefile 能够通过自动化针对每个文件的重复命令,节省时间。
能够让你快速掌握程序的依赖结构,易于管理,最大限度地减少了简单的重复性工作和重写工作。

我们来做个对比,如果使用普通编译过程:
$ gcc -c -o director.o director.c
$ gcc -c -o file.o file.c
$ gcc -c -o main.o main.c
如果使用 Makefile:
$ gcc -o exefile main.o director.o file.

🔨 动手尝试:vi Makefile 并输入以下内容
exefile2: file.o director.o main.o
gcc -o exefile2 file.o director.o main.o
file.o: file.c
gcc -c -o file.o file.c
director.o: director.c
gcc -c -o director.o director.c
main.o: main.c
gcc -c -o main.o main.c
clean:
rm *.o exefile2 # 清除所有扩展名为 .o 的文件和名为 exefile2 的可执行文件。

* 使用 make clean 命令:清除所有 扩展名为 .o 的文件和名为 exefile2 的可执行文件。
你可以使用变量,简化你的 Makefile!编写起来会更简单:

cc :用于指定 C 语言编译器的名称,通常为 gcc,可以它来指定编译器:
cc = gcc # 指定 C 编译器 gcc
cc = g++ # 指定 C++ 编译器 g++
target :设置 Makefile 最终要生成的目标文件
target=movie
也可以定义多个 target 生成多个目标文件,并为每个 target 指定依赖关系和构建规则。
object :用于指定所有的目标文件(object file),通常以“.o”为扩展名,使编写 prerequisites 和 recipe 更容易。
object=main.o file.o director.o
❓ 一些问题:
为什么没有 main.o film.o director.o 的 recipe?
make 会推导 recipe,因此没有必要写出每一个 recipe 来编译每个 c 文件。
提到了.o 文件,但没有为它制定规则?因为 make 使用了一个隐含的规则。
隐含规则:如果你需要创建或更新一个新的.o文件,找到一个同名的 .c 文件并使用 cc -c 命令。
需要 film.o -> film.o 不存在?-> 找到 film.c -> 执行:cc -c film.c -o film.o
cc 的含义和 Linux 上的 gcc 相同:
$(objects) : film.h
即使头文件改变了,它也会创建一个新的对象文件。
.PHONY : clean :伪目标
把它看成是 recipe 的简单表示,而不是实际文件的名称。这里的 recipe:
rm $(target) $(objects)
删除目标文件和当前目录下的所有对象文件。
为什么使用 PHONY?为了避免意外情况!比如:如果你的一个文件叫 clean 怎么办?
【一个经常出现的错误】
Makefile:17: ***缺少分隔符。 停止。
在第一章中,我们强调了在编写Makefile时,命令部分应该以TAB字符开始。上面的错误是因为我们没有使用TAB字符,所以我们无法判断make是否是一个命令。
解决方法:在第17行(接近结尾处)将命令改为以TAB字符开始。
make。*** 没有规则来制作目标 "io.h","read.o "需要。停止。
上述错误是由于依赖关系的问题:read.c被定义为依赖io.h,但io.h没有找到。
行动:调查依赖关系中定义的io.h是否真的存在。如果不存在,想想为什么不存在。你也可以尝试再次运行make dep来重新创建依赖关系。
Makefile:10: ***命令在第一个目标前开始。 停止。
上面的错误是一个模糊的错误信息,说 "命令在第一个目标之前开始"。根据我的经验,造成这个错误的原因似乎是在使用'\'标记多行长句时误用了'\':'\'部分应该是该行的最后一个字符,但如果你不小心在'\'后面加了几个空格,你就不可避免地会出现上述错误。
解决方法:在第10行(接近结尾处),如果有一个'\'字符,确保它是该行的最后一个字符,即删除'\'字符之后的一切(主要是空格)。
如果你运行make,你将不会得到你想要的可执行文件,而且它的行为会很奇怪。例如,它的行为将与make clean相同。
你必须记住,make并不是一个天才。当 make 读取 Makefile 的内容时,它认为它看到的第一个目标就是它应该产生的结果文件。 所以,如果你把 Makefile 的 clean 部分作为 Makefile 的第一个目标,你会得到上述结果。
解决方法:在例7.1中,我们创建了一个不必要的目标,叫做all。所以,要么把你想生成的结果文件作为第一个目标,要么像例7.1那样,创建一个像all一样的假目标。把 make clean 和 make dep 这样的东西放在 Makefile 的最后是安全的。
重新编译一个先前编译过的文件而不去修正它。
这种行为是因为make不知道依赖关系,也就是你没有设置它们。结果,make认为它的工作是编译所有文件并创建一个可执行文件。
解决方法:你需要设置目标文件、源文件和头文件的依赖关系。说gccmakedep *.c会在Makefile的末尾自动创建依赖关系。对于所有其他文件,你需要适当地设置依赖关系。
main.o : main.c io.h read.o : read.c io.h write.o : write.c io.h使用gcc时可能出现的错误信息!
程序在运行时展现程序内部发生了什么?出现 BUG 的时候便于我们知道在哪。
为了找到 BUG,gdb 做四种事:
① 程序运行时,各种条件设定后,程序开始。
② 遇到特定的条件时使程序暂停
③ 程序暂停时,检查发生了什么事
④ 在程序内部把 BUG 修改完后,继续运行找其他出现的 BUG
具有上面的特性的 GDB 是找到程序中 BUG 的利器!
运行 GDB 前编译方法及实行:
编译 (Compile) :
Debug 之前,编译将要 debug 的程序的 debugging 信息。这样就能使用 GDB 运行使用的变量和运行的函数了。举个例子:
gcc -g test.c -o test
运行 GDB:
GDB 运行程序名
为了在 GDB 中查看 C 的调试信息,需要使用 -g 选项编译 C 程序:
$ gcc -g test
要使用 GDB 调试编译后的 C 程序,按照以下步骤进行:
$ gdb program
此时就能看到用于输入 GDB 命令的提示符了:
(gdb)
我们先来创建一个用来演示 GCD 调试功能的目录:

既然要调试,我们就必须要有个代码,我们这里写一个数字累加的代码:

🚩 运行结果:

结果是5050,没有问题。如果我们代码出现了一些问题需要我们调试,我们就可以使用 gdb。
如果此时你出现了报错,说什么不支持 for 循环里面定义变量,你可以输入:
$ gcc hello.c -o hello -std=c99
在你当前的代码目录下直接执行 gcb + 形成的可执行程序:
$ gdb [可执行程序]
此时就进入了 gdb 的调试命令行中:

(如果想退出,直接按 quit 就可以退出了)
gcb 读取我们的 hello 程序时出现了 "没有调试符号被发现" 的警告:

这是什么意思呢?
我们的 gdb 中有一条命令叫做 list(简写成 l),但是我们输入后出现以下提示:

因为 —— 默认形成的可执行程序无法调试!!!
相信大家都知道,C语言的发布方式有两种,一种是 debug 一种是 release。
我们在 VS 里面可以直接调的原因是,VS2019 的默认集成环境是 debug。
而在我们的 Linux 中的默认集成环境是 release,换言之,
在我们 Linux 中如果你想发布一个程序,可以直接发布,无需加任何选项。
但是如果你 想调试,以 debug 形式发布,那么你就需要在编译时在后面添加一个 -g 选项:
$ gcc hello.c -o hello.g _g

🔺 总结:Linux 默认形成的可执行程序是动态链接且是 release 方式发布。
release 和 debug 的区别:你的可执行程序里本来就有调试信息, 只是 debug 中才有。
首先,这两个版本也都是可以运行的:

并且我要告诉你的是:debug 版本比 release 版本多几千个字节,这是什么?
毫无疑问,这些就是一个可执行程序的调试信息,它在 debug 版本中有所显现。
📚 如果你想看调试信息,你可以输入:
$ readelf -S [可执行程序] # 以段的形式读取可执行程序,用于显示读取ELF文件中信息
💭 我们先看看 release 版的:

💭 我们再来读一读 debug 版的:

因为 debug 版本是能给你的可执行程序添加调试信息的,所以体积自然比 release 版本要大些。
所以我们调试的得是 debug 版本的可执行程序,预备工作全部做好,下面我们来正式学习 gdb。
现在我们是 debug 版本了,我们也顺理成章地能够使用前面我们说的 list 了。
(gdb) list [n] # 显示代码,可带行号
(gdb) list [function] # 显示带某函数的代码块
(gdb) list [begin, end] # 显示区间内的代码
...
💭 操作演示:

一般在 VS 下调试的时候,除了让你看到代码,还会让你看到进行到了哪里,这里也是一样的。
你按下回车后,gdb 会自动记录你的上一条指令,直接按回车就是上一条命令:

(这么做就能把代码从第一行开始,将所有代码块逐个显示出来了)
💭 假设我们想在下面代码的第15行处打个断点:

这要是放在 VS 下我们直接滑鼠选中对应行然后无脑按 F11 就行了。
而在 gdb 下打断点的方式是用 breakpoint:
(gdb) breakpoint [n] # 在第n行处打一个断点
💭 操作演示:我们在代码第15行打个断点看看:

此时如果你想查看断点,可以使用 info 查看你打的断点:
(gdb) info b # 查看断点
💭 操作演示:查看断点信息

我们再在第17行新增一个断点,此时我们就能看到两个断点了:

如果想要删除断点,在 VS 下我们直接再点以下小红点就搞定了:
(图形化界面无疑是成功的)
但是在 gdb 中,我们需要知道要删除的断点的编号:
(gdb) d [Num] # 删除Num号断点
💭 操作演示:删除1号断点(记不得要删除的断点的编号可以 info b)

此时 1 号断点已被成功删除,再次删除则会显示已经没有这个断点:

准备开始调试,记得把刚才删除的断点再打回去,调试的指令如下:
(gdb) run # 开始调试
💭 操作演示:输入完 r 按回车开始调试:
(此时就悬停在了第15行)
如果我们把场上断点全部干掉了,此时按 r 调试程序就会直接跑完:
(这和 VS 也是一样的)
如果你想查看变量的内容,我们可以使用 print 命令:
(gdb) print [val]
💭 操作演示:查看变量内容

💭 操作演示:逐语句
如果想逐语句执行(逐语句即一步一步往后走),逐语句指令如下:
(gdb) step # 逐语句

我们 s 两次后,此时走到了函数的调用处。此时如果你不想进入该函数,就不要再按 s 逐语句了。
此时我们应该逐过程执行,我们可以使用 next 命令:
(gdb) next # 逐过程
💭 操作演示:逐过程

我们在 VS 中调试代码的时候,有时候要 细 ♂ 细 ♂ 观 ♂ 察 某个变量时,我们会打开监视窗口:

在 gdb 下我们就可以使用 display 常显示来监视:
$ display [val] # 监视一个变量,每次停下来都显示它的值
$ display [v1, v2, v3...] # 同时添加多个变量,用括号隔开
💭 操作演示:常显示 i 变量

当然,我们也可以同时监视多个值:
(同时常显示三个变量)
直接输入 display 可以查看监视列表:
$ display # 查看当前监视列表
💭 操作演示:查看监视列表

如果想把某个变量从监视窗口移除,我们可以使用 undisplay:
$ undisplay [n] # 删除n号变量
💭 操作演示:删除3号变量

我们调试的时候在文本特别大的时候我们有时候会跳转,VS 里我们可以直接拖动箭头跳转。
gdb 调试下我们可以使用 until 指令跳转到指定行:
$ until [n] # 跳转到指定行
💭 操作演示:跳转到20行

如果想从一个断点跳转至另一个断点,我们可以使用 c:
$ c # 直接跳转到另一个断点
有时候难免手贱不小心进了不想进入了函数,就比如不小心逐语句进了 printf 函数。
这个在 VS 下逐语句是不会进去的,但是在 Linux 下会进入,此时如果你反悔了象出来,
就可以输入 finish,它可以做到直接执行完成一个函数就停下来。
$ finish # 执行到当前函数返回,然后停下来等待命令
掌握上面单独介绍的 b、d、l、s、n、display、until、r、c、finish 其实就差不多了。
还有一些 gdb 的指令我们上面没有介绍,这里做一个整合:
- list/l 行号:显示binFile源代码,接着上次的位置往下列,每次列10行。
- list/l 函数名:列出某个函数的源代码。
- r 或 run:运行程序。
- n 或 next:单条执行。
- s或step:进入函数调用。
- break(b) 行号:在某一行设置断点。
- break 函数名:在某个函数开头设置断点。
- info break :查看断点信息。
- finish:执行到当前函数返回,然后挺下来等待命令。
- print(p):打印表达式的值,通过表达式可以修改变量的值或者调用函数。
- p 变量:打印变量值。
- set var:修改变量的值。
- continue(或c):从当前位置开始连续而非单步执行程序。
- run(或r):从开始连续而非单步执行程序。
- delete breakpoints:删除所有断点。
- delete breakpoints n:删除序号为n的断点。
- disable breakpoints:禁用断点。
- enable breakpoints:启用断点。
- info(或i) breakpoints:参看当前设置了哪些断点。
- display 变量名:跟踪查看一个变量,每次停下来都显示它的值。
- undisplay:取消对先前设置的那些变量的跟踪。
- until X行号:跳至X行。
- breaktrace(或bt):查看各级函数调用及参数。
- info(i) locals:查看当前栈帧局部变量的值。
- quit:退出gdb。

📌 [ 笔者 ] 王亦优
📃 [ 更新 ] 2023.3.21
❌ [ 勘误 ] /* 暂无 */
📜 [ 声明 ] 由于作者水平有限,本文有错误和不准确之处在所难免,
本人也很想知道这些错误,恳望读者批评指正!
| 📜 参考资料 Microsoft. MSDN(Microsoft Developer Network)[EB/OL]. []. . 百度百科[EB/OL]. []. https://baike.baidu.com/. GCC 컴파일러 에러 메세지 리스트(Error Message List) :: Block Busting |
我正在尝试在我的centos服务器上安装therubyracer,但遇到了麻烦。$geminstalltherubyracerBuildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingtherubyracer:ERROR:Failedtobuildgemnativeextension./usr/local/rvm/rubies/ruby-1.9.3-p125/bin/rubyextconf.rbcheckingformain()in-lpthread...yescheckingforv8.h...no***e
几个月前,我读了一篇关于rubygem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:
我正在玩HTML5视频并且在ERB中有以下片段:mp4视频从在我的开发环境中运行的服务器很好地流式传输到chrome。然而firefox显示带有海报图像的视频播放器,但带有一个大X。问题似乎是mongrel不确定ogv扩展的mime类型,并且只返回text/plain,如curl所示:$curl-Ihttp://0.0.0.0:3000/pr6.ogvHTTP/1.1200OKConnection:closeDate:Mon,19Apr201012:33:50GMTLast-Modified:Sun,18Apr201012:46:07GMTContent-Type:text/plain
电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。 准备工作: 1、U盘一个(尽量使用8G以上的U盘)。 2、一台正常联网可使用的电脑。 3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。 4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。 U盘启动盘制作步骤: 注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注
之前在培训新生的时候,windows环境下配置opencv环境一直教的都是网上主流的vsstudio配置属性表,但是这个似乎对新生来说难度略高(虽然个人觉得完全是他们自己的问题),加之暑假之后对cmake实在是爱不释手,且这样配置确实十分简单(其实都不需要配置),故斗胆妄言vscode下配置CV之法。其实极为简单,图比较多所以很长。如果你看此文还配不好,你应该思考一下是不是自己的问题。闲话少说,直接开始。0.CMkae简介有的人到大二了都不知道cmake是什么,我不说是谁。CMake是一个开源免费并且跨平台的构建工具,可以用简单的语句来描述所有平台的编译过程。它能够根据当前所在平台输出对应的m
网络编程套接字网络编程基础知识理解源`IP`地址和目的`IP`地址理解源MAC地址和目的MAC地址认识端口号理解端口号和进程ID理解源端口号和目的端口号认识`TCP`协议认识`UDP`协议网络字节序socket编程接口`sockaddr``UDP`网络程序服务器端代码逻辑:需要用到的接口服务器端代码`udp`客户端代码逻辑`udp`客户端代码`TCP`网络程序服务器代码逻辑多个版本服务器单进程版本多进程版本多线程版本线程池版本服务器端代码客户端代码逻辑客户端代码TCP协议通讯流程TCP协议的客户端/服务器程序流程三次握手(建立连接)数据传输四次挥手(断开连接)TCP和UDP对比网络编程基础知识
1.postman介绍Postman一款非常流行的API调试工具。其实,开发人员用的更多。因为测试人员做接口测试会有更多选择,例如Jmeter、soapUI等。不过,对于开发过程中去调试接口,Postman确实足够的简单方便,而且功能强大。2.下载安装官网地址:https://www.postman.com/下载完成后双击安装吧,安装过程极其简单,无需任何操作3.使用教程这里以百度为例,工具使用简单,填写URL地址即可发送请求,在下方查看响应结果和响应状态码常用方法都有支持请求方法:getpostputdeleteGet、Post、Put与Delete的作用get:请求方法一般是用于数据查询,
Ⅰ软件测试基础一、软件测试基础理论1、软件测试的必要性所有的产品或者服务上线都需要测试2、测试的发展过程3、什么是软件测试找bug,发现缺陷4、测试的定义使用人工或自动的手段来运行或者测试某个系统的过程。目的在于检测它是否满足规定的需求。弄清预期结果和实际结果的差别。5、测试的目的以最小的人力、物力和时间找出软件中潜在的错误和缺陷6、测试的原则28原则:20%的主要功能要重点测(eg:支付宝的支付功能,其他功能都是次要的)80%的错误存在于20%的代码中7、测试标准8、测试的基本要求功能测试性能测试安全性测试兼容性测试易用性测试外观界面测试可靠性测试二、质量模型衡量一个优秀软件的维度①功能性功
目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称
@作者:SYFStrive @博客首页:HomePage📜:微信小程序📌:个人社区(欢迎大佬们加入)👉:社区链接🔗📌:觉得文章不错可以点点关注👉:专栏连接🔗💃:感谢支持,学累了可以先看小段由小胖给大家带来的街舞👉微信小程序(🔥)目录自定义组件-behaviors 1、什么是behaviors 2、behaviors的工作方式 3、创建behavior 4、导入并使用behavior 5、behavior中所有可用的节点 6、同名字段的覆盖和组合规则总结最后自定义组件-behaviors 1、什么是behaviorsbehaviors是小程序中,用于实现