【Linux】--make/makefile--gcc/g++/gdb

2023-11-19

make/makefile

概念

make是Linux下的一条指令,而Makefile是一个文件,可以简单理解为make就是执行Makefile里面写的目的。

会不会去写Makefile可以侧面的看出一个人是否具备完成大型工程的能力

假设现有一个工程里面有上千上万个源文件,那么就可以使用Makefile定义一系列的规则来指定,哪些文件需要先编译,哪些文件后编译。当我们写好Makefile后,只需要一个make命令就可以让整个工程完全自动编译,极大地提高了效率

也就是说,make是一个解释Makefile中指令的命令工具,两者搭配使用就可以完成项目自动化构建

Makefile

那么Makefile应该怎么样去写呢,那就得先了解一下Makefile的原理了。

在Makefile里面我们需要去定义一个方法时,就得考虑到两个因素,依赖关系依赖方法。举例:当我们编写好一个源文件时,我们需要对它进行编译生成可执行文件,那么可以把这个可执行文件看做是目标文件,想要生成它就得依赖我们写好的源文件,那么我们就可以把源文件看做是依赖文件。那有了源文件后,我们需要通过编译工具编译的方法才能生成可执行文件,那这个方法我们就可以看做是依赖方法。
在这里插入图片描述

首先我写好了一个.c源文件,并且创建了一个makefile文件,按照之前学的,这是我们可以用gcc直接去编译生成可执行文件,但是如果每一次都去写gcc的指令就会很麻烦,因此我们可以考虑将这条指令放进makefile里面,我们只需要执行一下make指令就可以自动调用了。

image-20221029150244348

编写的格式就跟上面所讲,需要有依赖关系、依赖方法。目标文件的后面必须要有跟上一个冒号,并且依赖方法另起一行必须以Tab键开头,不能是几个空格

当我们写好之后,make一下看看效果

image-20221029150538443

可以看到当我们make一下后,系统会自动执行在makefile里面的指令,生成一个可执行文件。但是现在当我们再make一下,还会再次执行吗?

image-20221029150712364

报错了,这是为什么呢?

make指令这点就带来了好处,当我们的源文件没有被修改时并且可执行文件已经生成时,make就会禁止你的再一次编译,那么问题来了make怎么知道源文件有没有被修改呢,这就得说到文件的属性了。

image-20221029151230977

当我们去查看文件的属性时会发现,文件都会有三个时间属性,那么这三个分别代表什么呢

  1. Access:文件最近一次被访问的时间
  2. Modify:文件最后一次被修改的时间
  3. Change:文件的状态被修改的时间

当我们的源文件没有被修改时并且可执行文件已经生成时,此时我们在make一下,make会去比较源文件和已有的可执行文件的被修改时间,如果可执行文件的比源文件的要晚,那么make不再执行编译指令,反之同理

那如果我就想让它执行呢,这时候就引入了一个makefile中的一个关键字:.PHONY(伪目标,该目标总是被执行)

image-20221029151900911

image-20221029151918638

可以看到此时,该条指令就可以无限执行了。

make

make是如何工作的呢

  1. make首先会在当前目录下找“makefile”或“Makefile”的文件
  2. 找到之后,它会找文件中的第一个目标文件,并把该依赖方法执行生成最终的目标文件
  3. 假设现在目标文件为hello,依赖文件为hello.o,但是此时目录下没有hello.o文件,那么make就会继续往下找以hello.o为目标文件的依赖关系,以此类推。
  4. 也就是说make会一层一层的去找文件的依赖关系,直到最终编译出第一个目标文件
  5. 如果最后被依赖的文件找不到,那么make会直接退出并报错

gcc/g++/gdb

gcc/g++

众所周知,当我们使用C/C++语言写好代码后,我们的先让源文件经过预处理,编译,汇编,链接几个步骤后才会生成一个二进制机器语言,这样计算机才能看得懂并且执行。那么在Windows下我们可以利用集成开发软件去帮我们完成这几个步骤,同理在Linux下我们就需要借用gcc/g++工具来帮我们完成。

预处理

预处理的主要功能包括宏定义,文件包含,条件编译,去注释等操作

我们在编写代码时包含的头文件就会在这个步骤进行展开,宏定义会进行替换,我们写的注释也会在这个步骤去除

在Linux中该步骤生成的文件一般以.i为后缀,我们可以通过命令去完成这个步骤,需要用到-E选项

gcc -E main.c -o main.i

编译

这个步骤,首先gcc/g++会检查代码是否有错误,以及确定代码的实际要做的工作,如果一切正常无误后,gcc/g++就会把代码翻译成汇编语言

在Linux中该步骤生成的文件一般以.s为后缀,我们可以通过命令去完成这个步骤,需要用到-S选项

gcc -S main.c -o main.s

汇编

汇编阶段就是把生成的.s文件转为目标文件也就是生成机器可以识别的二进制语言

在Linux中该步骤生成的文件一般以.o为后缀,我们可以通过命令去完成这个步骤,需要用到-c选项

gcc -c main.c -o main.o

链接

这个阶段,目标文件会和系统库进行连接生成一个可执行程序

Linux中库的命名:去掉前缀lib,去掉后缀.so,剩下的就是库的名称

静态链接

这个链接方式下,代码会将静态链接库拷贝一份到最终的可执行程序中,这样虽然提高了效率但是会是可执行程序的空间变得很大,不利于大型项目

在linux下想要生成静态链接就要在后面加上 -static选项,并且要确保已经安装了C/C++的静态库

sudo yum install glibc-static//安装C静态库
sudo yum install libstdc++-static//安装c++静态库

image-20221029155826995

image-20221029155930787

动态链接

这个链接方式下,在可执行程序执行的时候,会将动态链接库映射到一块虚拟空间中,然后根据需要再去找对应的函数代码,这样可以极大地缩小可执行程序的空间。

Linux下默认的链接方式是动态链接,依赖的动态库一般为.so为后缀

image-20221029155056579

image-20221029155131557

对比上面的静态链接明显大小小很多

选项操作

  1. -E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面
  2. -S 编译到汇编语言不进行汇编和链接
  3. -c 编译到目标代码
  4. -o 重命名可执行文件
  5. -static 此选项对生成的文件采用静态链接
  6. -g 生成调试信息。GNU 调试器可利用该信息

gdb

在平时Windows下我们使用想vs2019这样的话编译器时都会有一个调试的功能,让我们发现代码的错误之处。

同理在Linux下也有,这个调试工具就叫gdb,首先我们要安装一下gdb

sudo yum install gdb

安装好之后当我们直接运行gdb调试我们的可执行代码时会发现出问题了

image-20221029161043362

因为程序会分为两个版本,一个是Debug版,一个是Release版

在debug模式下我们才可以对代码进行调试,但是Linux默认的模式是release,所以在生成可执行文件时需要加上-g选项,让它生成的可执行程序是debug模式才可以进行调试

image-20221029161431544

自此我们才算是正式进入到了gdb中

那么对于gdb而言也有属于自己的指令

  1. list/l 行号:显示binFile源代码,接着上次的位置往下列,每次列10行。
  2. list/l 函数名:列出某个函数的源代码。
  3. r或run:运行程序。
  4. n 或 next:单条执行。
  5. s或step:进入函数调用
  6. break(b) 行号:在某一行设置断点
  7. break 函数名:在某个函数开头设置断点
  8. info break :查看断点信息。
  9. finish:执行到当前函数返回,然后挺下来等待命令
  10. print§:打印表达式的值,通过表达式可以修改变量的值或者调用函数
  11. p 变量:打印变量值。
  12. set var:修改变量的值
  13. continue(或c):从当前位置开始连续而非单步执行程序
  14. delete breakpoints:删除所有断点
  15. delete breakpoints n:删除序号为n的断点
  16. disable breakpoints:禁用断点
  17. enable breakpoints:启用断点
  18. info(或i) breakpoints:参看当前设置了哪些断点
  19. display 变量名:跟踪查看一个变量,每次停下来都显示它的值
  20. undisplay:取消对先前设置的那些变量的跟踪
  21. until X行号:跳至X行
  22. breaktrace(或bt):查看各级函数调用及参数
  23. info(i) locals:查看当前栈帧局部变量的值
  24. quit:退出gdb

接下来我们一个一个看

首先显示我们的代码,需要用到l指令

image-20221029161748856

该指令后面也可以加上行号或者函数名,显示指定内容

接下来我们需要为程序设置一个断点,如果不设置直接执行程序就会跑完,这是要用到b + 行号指令,设置好之后我们可以通过info b查看我们已经设置好的断点

image-20221029162144207

还可以通过d + 序号删除对应的断点,注意这里的序号不是行号,而是我们设置好断点后每个断点通过info b查看之后每个断点对应的序号

image-20221029162403468

设置好断点后此时我们就可以运行起来了,输入r运行程序(对应VS2019F5),如果执行到某行,该行有函数我们想进入函数调试就需要输入s(对应vs2019的F11),不想进入函数就输入n(对应vs2019F10)

image-20221029163241939

image-20221029163327344

那么现在如果我进入到函数里面了,但是函数里有千次的循环怎么直接执行完函数退出呢,需要输入finish

image-20221029163515058

那么当我们进入到一个函数时,我们查看函数的变量值和变量的地址,这时候需要输入p + 变量名p + &变量名

image-20221029163746214

如果想每次执行完一条语句就显示出想查看的变量值,就需要用display + 变量名,不想看了想删除掉就使用undisplay + 变量名

image-20221029164018697

剩下的一些可以自己去探索了,在上面所讲的都是一些比较常用的。

总结

学习到这里已经是可以再Linux下进行代码编写了

接下来就得学习一些系统底层的知识了

每天进步一点淡

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Linux】--make/makefile--gcc/g++/gdb 的相关文章

  • 我应该使用哪个 Linux 发行版作为 Xen 主机? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我为家庭办公室订购了一台服务器 我想用 Xen 对其进行分区 我认为这将使事情保持干净并且更容易维护 我将运行 MySQL PostgreSQL
  • Linux、ARM:为什么仅当启动时存在 I2C GPIO 扩展器时才创建 gpiochip

    在 imx6sx 硬件平台 NXP 嵌入式 ARM 上使用 Linux 3 14 52 问题是设备树中指定的 PCF8575 I2C GPIO 扩展器不会实例化为 sys class gpio 结构中的设备 除非它们在内核启动期间存在 这些
  • 在 shell 脚本中查找和替换

    是否可以使用 shell 在文件中搜索然后替换值 当我安装服务时 我希望能够在配置文件中搜索变量 然后在该值中替换 插入我自己的设置 当然 您可以使用 sed 或 awk 来完成此操作 sed 示例 sed i s Andrew James
  • CentOS目录结构是树形的吗?

    CentOS 上有相当于树的东西吗 如果你的 Centos 系统上没有安装 tree 无论如何我通常建议服务器设置使用最小安装磁盘 你应该在命令行中输入以下内容 yum install tree y 如果没有安装 那是因为您没有正确的存储库
  • 如何在 Linux 主机上的 docker 容器中挂载目录 [重复]

    这个问题在这里已经有答案了 我想将一个目录从 docker 容器挂载到本地文件系统 该目录是网站根目录 我需要能够使用任何编辑器在本地计算机上编辑它 我知道我可以跑docker run v local path container path
  • Linux 上的 Python 3.6 tkinter 窗口图标错误

    我正在从 Python GUI 编程手册 学习 Python GUI 某项任务要求我通过将以下代码添加到我的配方中来更改窗口图标 Change the main windows icon win iconbitmap r C Python3
  • python:numpy 运行脚本两次

    当我将 numpy 导入到 python 脚本中时 该脚本会执行两次 有人可以告诉我如何阻止这种情况 因为我的脚本中的所有内容都需要两倍的时间 这是一个例子 usr bin python2 from numpy import print t
  • 如何从linux命令行运行.exe可执行文件? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我在 Windows 中有一个 abc exe 可执行文件 我可以使用 DOS 命令提示来执行此应用程序 并为其提供一些运行时变量 我想从
  • 无法仅在控制台中启动 androidstudio

    你好 我的问题是下一个 我下载了Android Studio如果我去 路径 android studio bin 我执行studio sh 我收到以下错误 No JDK found Please validate either STUDIO
  • Scrapy FakeUserAgentError:获取浏览器时发生错误

    我使用 Scrapy FakeUserAgent 并在我的 Linux 服务器上不断收到此错误 Traceback most recent call last File usr local lib64 python2 7 site pack
  • Inotify linux 监视子目录

    是否可以以这种模式监视目录 storage data usernames Download gt storage data Download 我需要监视每个用户的下载文件夹中是否进行了更改 也许我需要创建所有路径的列表 将其放入数组中 并在
  • 每个进程每个线程的时间量

    我有一个关于 Windows 和 Linux 中进程和线程的时间量子的问题 我知道操作系统通常为每个线程提供固定的时间量 我知道时间量根据前台或后台线程而变化 也可能根据进程的优先级而变化 每个进程有固定的时间量吗 例如 如果操作系统为每个
  • Linux shell 脚本:十六进制数字到二进制字符串

    我正在 shell 脚本中寻找一些简单的方法来将十六进制数字转换为 0 和 1 字符的序列 Example 5F gt 01011111 是否有任何命令或简单的方法来完成它 或者我应该为其编写一些开关 echo ibase 16 obase
  • sleep 0 有特殊含义吗?

    我看到很多用法sleep 0在我的一个客户项目中 代码看起来像这样 while true sleep 0 end 阅读一些像这样的答案this https stackoverflow com questions 3727420 signif
  • 无需 cron 在后台发送邮件

    我想知道是否有一种方法可以运行 PHP 循环 以便在后台向订阅者发送几百封电子邮件 我的目标是格式化新闻通讯 单击发送 然后关闭浏览器或更改页面 当然 发送电子邮件的实际过程将在后台运行 不会因浏览器关闭而中断 我知道这可以通过 cron
  • 如何在特定 systemd 服务重新启动时触发自定义脚本运行

    我想知道如何安排自定义脚本在重新启动服务时运行 我的用例是 每当重新启动 Tomcat 服务时 我都必须运行多个命令 我想知道是否有一种方法可以编写脚本并安排它在重新启动 Tomcat 服务时运行 我已将 tomcat 脚本设置为 syst
  • Linux 桌面快捷方式和安装图标

    我需要添加什么到我的 spec文件来创建桌面快捷方式并在安装过程中为快捷方式分配一个图标 rpm 如果需要脚本 一个示例将非常有帮助 您在 Linux 下使用 desktop 文件作为图标 图标放置的位置取决于您使用的发行版和桌面环境 由于
  • 如何使用 sed 仅删除双空行?

    我找到了这个问题和答案 https stackoverflow com questions 4651591 howto use sed to remove only triple empty lines关于如何删除三重空行 但是 我只需要对
  • arm-linux-gnueabi 编译器选项

    我在用 ARM Linux gnueabi gcc在 Linux 中为 ARM 处理器编译 C 程序 但是 我不确定它编译的默认 ARM 模式是什么 例如 对于 C 代码 test c unsigned int main return 0x
  • QFileDialog::getSaveFileName 和默认的 selectedFilter

    我有 getSaveFileName 和一些过滤器 我希望当用户打开 保存 对话框时选择其中之一 Qt 文档说明如下 可以通过将 selectedFilter 设置为所需的值来选择默认过滤器 我尝试以下变体 QString selFilte

随机推荐

  • 判断一个IP地址是不是单播地址

    1 组播地址 2 单播地址 1 2
  • C++_生成随机字符串

    include
  • Vite配置跨域代理

    Vite 配置跨域代理 修改vite config js文件 import defineConfig from vite import react from vitejs plugin react https vitejs dev conf
  • Xilinx AXI-memory接口 转 AXI-stream 接口(含源码)

    AXI memory接口 转 AXI stream 接口 AXI memory接口介绍 具体详情可以查看源码 AXI memory接口介绍 从图中我们可以看出memory接口有5个通道 分别是读地址通道 写地址通道 写响应通道 读数据通道
  • 华为OD两轮技术面试

    华为OD面试 1性格测试 选积极向上的选项 注意 性格测试也会挂人 我一个朋友性格测试就没过 2机试 一道变成题目 1h 用例60 通过即可 任给一个数组 元素有20M 1T 300G之类的 其中1T 1000G 1G 1000M 按从小到
  • 数据库事务锁详解

    前言 上篇说到数据库事务中的特性ACID和4个隔离级别 今儿就来看一下事务中的锁 MySQL中的锁 锁是MySQL在服务器层和存储引擎层的并发控制 锁可以保证数据并发访问的一致性 有效性 锁冲突也是影响数据库并发访问性能的一个重要因素 My
  • 并发编程4 - 线程状态、死锁及ReentrantLock

    文章目录 一 再述线程状态转换 二 多把锁与线程活跃性问题 1 多把锁 2 活跃性 三 ReEntrantLock 1 基本用法 2 可重入 3 可打断 4 锁超时 5 公平锁 6 条件变量 一 再述线程状态转换 情况1 New RUNNA
  • JAVA数据结构——利用图的广度优先遍历搜索算法确定无向连通图的连通分量

    分析 如果这个无向图是非连通图的时候 从图的一个顶点没法访问这个图的所有顶点 只能访问包含该顶点的连通分量中的所有顶点 所以从无向图的每个连通分量中的一个顶点开始遍历图 则可求得无向图的所有连同分量 如图则是非连通的无向图 我们只需要从第一
  • (Python笔记)使用Python解析HEX文件的内容

    需要用到binascii库 binascii库中包含了很多在二进制和二进制表示的各种ASCII码之间转换的方法 Code import binascii HEX path r 1 HEX with open HEX path rb as f
  • Python 实现列队

    1 列队定义 队列是项的有序结合 其中添加新项的一端称为队尾 移除项的一端称为队首 当一个元素从队尾进入队列时 一直向队首移动 直到它成为下一个需要移除的元素为止 最近添加的元素必须在队尾等待 集合中存活时间最长的元素在队首 这种排序成为
  • Flutter个推推送Android端,退出应用后收到消息报错

    场景 Android手机 iOS没有测试 1 集成getuiflut 2 返回退出APP 3 发送推送透传消息 报错 Tried to send a platform message to Flutter but FlutterJNI wa
  • (八)nginx反向代理功能

    nginx反向代理概念 反向代理也称reverse proxy 指的就是代理外网用户请求到内部指定web服务器 并将数据返回给用户的一种方式 nginx除了可以在企业提供高性能的web服务之外 另外还可以将本身不具备的请求通过某种预定的协议
  • centos7安装配置fdfs时service fdfs_storaged start启动不成功

    service fdfs storaged start 命令启动后 提示是启动失败 ps ajx grep fdfs 查看不到 fdfs storaged启动信息 解决方法 当时解压缩时 fdfs storaged解压失败 自己当时没注意
  • ERROR:105: Unable to locate a modulefile for 'xxx'

    查看可用的 module module avail 将xxx替换为屏幕输出中已有的模块 转载于 https www cnblogs com zhyantao p 10462141 html
  • cunit单元测试

    一 安装cunit 1 1下载cunit https sourceforge NET projects cunit 下载最新安装包 1 2解压缩安装包 root Ubuntu1610 tar jxvf CUnit 2 1 3 tar bz2
  • HJ103 Redraiment的走法

    Redraiment是走梅花桩的高手 Redraiment可以选择任意一个起点 从前到后 但只能从低处往高处的桩子走 他希望走的步数最多 你能替Redraiment研究他最多走的步数吗 示例 2 5 1 5 4 5 输出 3 说明 6个点的
  • 从零开始的ESP8266探索(07)-使用Arduino for esp8266出现的问题小结

    文章目录 问题一 现象描述 原因分析 解决方法 问题二 现象描述 原因分析 解决方法 问题三 现象描述 原因分析 解决方法 实例应用 问题一 现象描述 使用 WiFi softAP 方法建立网络 有时候很难连接上 改用 WiFi begin
  • Android - Fragment 内嵌 ViewPager ,ViewPager各个页面使用Fragment第二次进入空白界

    直接说解决办法 在activity中创建adapter时fragment管理参数用supportFragmentManager 而在fragment中创建adpater时参数用childFragmentManager分开管理 就可以了 错误
  • 人工智能数学基础--极大似然估计

    极大似然估计 极大似然估计的原理 用一张图片来说明 如下图所示 原理 极大似然估计是建立在极大似然原理的基础上的一个统计方法 是概率论在统计学中的应用 极大似然估计提供了一种给定观察数据来评估模型参数的方法 即 模型已定 参数未知 通过若干
  • 【Linux】--make/makefile--gcc/g++/gdb

    文章目录 make makefile 概念 Makefile make gcc g gdb gcc g 预处理 编译 汇编 链接 静态链接 动态链接 选项操作 gdb 总结 make makefile 概念 make是Linux下的一条指令