当前位置: 首页 > news >正文

面试官问 Linux 编译调试?gcc 编译流程 + gdb 断点调试 + git 版本控制,连 Makefile 都标好了

在这里插入图片描述

个人头像
✨ 孤廖: 个人主页

🎯 个人专栏: 《C++:从代码到机器》

🎯 个人专栏: 《Linux系统探幽:从入门到内核》

🎯 个人专栏: 《算法磨剑:用C++思考的艺术》

折而不挠,中不为下


在这里插入图片描述

文章目录

  • 前言:
  • 正文:
    • 编译器gcc/g++
      • 预处理(宏替换)
      • 编译(⽣成汇编)
      • 汇编(⽣成机器可识别代码)
      • 链接(⽣成可执⾏⽂件或库⽂件)
      • 动态链接和静态链接
      • 静态库和动态库
    • ⾃动化构建-make/Makefile
    • Linux第⼀个系统程序−进度条
    • 版本控制器Git
    • 调试器 - gdb/cgdb使⽤
      • 常⻅使⽤
  • 结语:

前言:

Linux 的基础开发工具 继上篇的yum(软安装包) vim(编辑器),我们还剩gcc/g++ (编译器) gdb (调试器) git (版控制器) 该篇主要讲述这三个工具的使用 以完善Linux 的基础开发工具的体系

正文:

编译器gcc/g++

一个程序从源文件形成可执行程序 共分为四步

    1. 预处理(进⾏宏替换/去注释/条件编译/头⽂件展开等)
    1. 编译(⽣成汇编)
    1. 汇编(⽣成机器可识别代码)
    1. 链接(⽣成可执⾏⽂件或库⽂件)

格式 gcc [选项] 要编译的⽂件 [选项] [⽬标⽂件]

预处理(宏替换)

  • 预处理功能主要包括宏定义,⽂件包含,条件编译,去注释等。
  • 预处理指令是以#号开头的代码⾏。
  • 编译选项:
    格式 gcc [选项] 要编译的⽂件 [选项] [⽬标⽂件]
    eg:

gcc a.c -E a.i //预处理阶段
gcc a.i - S a.s //编译阶段 生成汇编 该选项的作⽤是让 gcc 在预处理结束后停⽌编译过程。
gcc a.s -c a.o // 汇编 生成机器可识别代码(01二进制语言)

  • 选项“-o”是指⽬标⽂件,“.i”⽂件为已经过预处理的C原始程序," .s " 指已生成汇编语言文件

这三步的指令选项 的记忆技巧 可以看键盘的左上角 ESC 不过注意细节S E 大写 c 小写

编译(⽣成汇编)

  • 在这个阶段中,gcc ⾸先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的⼯作,在检查⽆误后,gcc 把代码翻译成汇编语⾔。
  • ⽤⼾可以使⽤“-S”选项来进⾏查看,该选项只进⾏编译⽽不进⾏汇编,⽣成汇编代码。
    实例: gcc –S hello.i –o hello.s

汇编(⽣成机器可识别代码)

  • 汇编阶段是把编译阶段⽣成的“.s”⽂件转成⽬标⽂件
  • 读者在此可使⽤选项“-c”就可看到汇编代码已转化为“.o”的⼆进制⽬标代码了
  • 实例: gcc –c hello.s –o hello.o

链接(⽣成可执⾏⽂件或库⽂件)

在成功汇编之后,就进⼊了链接阶段。
实例: gcc hello.o –o hello

动态链接和静态链接

在我们的实际开发中,不可能将所有代码放在⼀个源⽂件中,所以会出现多个源⽂件,⽽且多个源⽂件之间不是独⽴的,⽽会存在多种依赖关系,如⼀个源⽂件可能要调⽤另⼀个源⽂件中定义的函数,
但是每个源⽂件都是独⽴编译的,即每个.c⽂件会形成⼀个.o⽂件,为了满⾜前⾯说的依赖关系,则需要将这些源⽂件产⽣的⽬标⽂件进⾏链接,从⽽形成⼀个可以执⾏的程序**。这个链接的过程就是静态链接

静态链接缺点:

  • 浪费空间:因为每个可执⾏程序中对所有需要的⽬标⽂件都要有⼀份副本,所以如果多个程序对同⼀个⽬标⽂件都有依赖,如多个程序中都调⽤了printf()函数,则这多个程序中都含有printf.o,所以同⼀个⽬标⽂件都在内存存在多个副本;
  • 更新⽐较困难因为每当库函数的代码修改了,这个时候就需要重新进⾏编译链接形成可执⾏程序。但是静态链接的优点就是,在可执⾏程序中已经具备了所有执⾏程序所需要的任何东西,在执⾏的时候运⾏速度快

为了解决静态链接的问题 前辈们提出了动态链接的概念并很好的设计这一过程

  • 动态链接的基本思想是把程序按照模块拆分成各个相对独⽴部分程序运⾏时才将它们链接在⼀起形成⼀个完整的程序,⽽不是像静态链接⼀样把所有程序模块都链接成⼀个单独的可执⾏⽂件。

下面我们查看一个程序 可以看到该可执行程序依赖一个C动态链接库

在这里插入图片描述

ldd 命令⽤于打印程序或者库⽂件所依赖的共享库列表

上面我们涉及到了 ”库“这一重要概念

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

静态库和动态库

  • 静态库是指编译链接时,把库⽂件的代码全部加⼊到可执⾏⽂件中,因此⽣成的⽂件⽐较⼤,但在运⾏时也就不再需要库⽂件了。其后缀名⼀般为“.a
  • 动态库与之相反,在编译链接时并没有把库⽂件的代码加⼊到可执⾏⽂件中,⽽是在程序执⾏时由运⾏时链接⽂件加载库,这样可以节省系统的开销。动态库⼀般后缀名为“.so”,如前⾯所述的libc.so.6 就是动态库gcc 在编译时默认使⽤动态库。完成了链接之后,gcc 就可以⽣成可执⾏⽂件,如下所⽰。 gcc hello.o –o hello
  • gcc默认⽣成的⼆进制程序,是动态链接的,这点可以通过 file 命令验证
  • Linux下,动态库XXX.so, 静态库XXX.a
  • Windows下,动态库XXX.dll, 静态库XXX.lib

这里需要需要注意的是 我们的云服务器一般是没有安装C/C++的静态库的(因为默认采用动态链接),可以采用以下指令安装

# Centos
yum install glibc-static libstdc++-static -y

eg:

在这里插入图片描述

⾃动化构建-make/Makefile

  • 会不会写makefile,从⼀个侧⾯说明了⼀个⼈是否具备完成⼤型⼯程的能⼒
  • ⼀个⼯程中的源⽂件不计数,其按类型、功能、模块分别放在若⼲个⽬录中,makefile定义了⼀系列的规则来指定,哪些⽂件需要先编译,哪些⽂件需要后编译,哪些⽂件需要重新编译,甚⾄于进⾏更复杂的功能操作
  • makefile带来的好处就是⸺“⾃动化编译”,⼀旦写好,只需要⼀个make命令,整个⼯程完全⾃动编译,极⼤的提⾼了软件开发的效率。
  • make是⼀个命令⼯具,是⼀个解释makefile中指令的命令⼯具.`⼀般来说,⼤多数的IDE都有这个命令,⽐如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可⻅,makefile都成为了⼀种在⼯程⽅⾯的编译⽅法。
  • make是⼀条命令,makefile是⼀个⽂件,两个搭配使⽤,完成项⽬⾃动化构建。
SRC=$(wildcard *.c)
BIN=proc.exe
OBJ=$(SRC:.c=.o)
RM =rm -r$(BIN):$(OBJ)gcc -o $@  $^%.o:$.cgcc -c $<.PHNOY:cleanclean:$(RM)   $(OBJ) $(BIN)

详细解释:

在这里插入图片描述

makefile 最重要的就是这三步

  • 依赖关系

上面的所有的可执行文件BIN依赖于所有的OBJ文件

  • 依赖方法:

gcc -o $@ $^ 就是 依赖方法
$^指向被依赖对象OBJ $@指向BIN

  • 项⽬清理
  • ⼯程是需要被清理的
  • 像clean这种,没有被第⼀个⽬标⽂件直接或间接关联,那么它后⾯所定义的命令将不会被⾃动执⾏,不过,我们可以显⽰要make执⾏。即命令⸺“make clean”,以此来清除所有的⽬标⽂件,以便重编译。

只有Make file 的第一个依赖 才可以直接使用make 默认执行

  • 但是⼀般我们这种clean的⽬标⽂件,我们将它设置为伪⽬标,⽤ .PHONY 修饰,伪⽬标的特性是,总是被执⾏的

在这里插入图片描述

Linux第⼀个系统程序−进度条

前提需要知道回车和换行的区别以及 缓冲区的概念 这里不做补充 自行查阅学习

【代码展示】:

#include <string.h>
#include <stdio.h>
#include <unistd.h>
#define NUM 101
#define STYLE '='
// vesion1
void process_v1()
{char buffer[NUM];memset(buffer, 0, sizeof(buffer));const char *lable="|/-\\";int len = strlen(lable);int cnt = 0;while(cnt <= 100){printf("[%-100s][%d%%][%c]\r", buffer, cnt, lable[cnt%len]);fflush(stdout);buffer[cnt]= STYLE;cnt++;usleep(50000);}printf("\n");
}
// verison2
void FlushProcess(double total, double current)
{char buffer[NUM];memset(buffer, 0, sizeof(buffer));const char *lable="|/-\\";int len = strlen(lable);static int cnt = 0;
// 不需要⾃⼰循环,填充#int num = (int)(current*100/total); // 11.0 / 1000int i = 0;for(; i < num; i++){buffer[i] = STYLE;}double rate = current/total;cnt %= len;printf("[%-100s][%.1f%%][%c]\r", buffer, rate*100, lable[cnt]);cnt++;fflush(stdout);
}
double total = 1024.0;
double speed = 1.0;
void DownLoad()
{double current = 0;while(current <= total){FlushProcess(total, current);// 下载代码usleep(3000); // 充当下载数据current += speed;
}printf("\ndownload %.2lfMB Done\n", current);
}
int main()
{DownLoad();return 0;
}

版本控制器Git

正常使用会三板斧就行了

  • git add
  • git commit ,
  • git push

如果想要详细的企业级开发工具Git 可以参考我之前所写的两篇文章
上:萌系学 Git:从提交规范到团队协作,工具喵教你玩转企业级工具
下:萌系学 Git:从提交规范到团队协作,工具喵教你玩转企业级工具

这两篇文章详细的介绍了 Git的发展历程 以及使用和企业级开发流程

调试器 - gdb/cgdb使⽤

  • 程序的发布⽅式有两种, debug 模式和 release 模式, Linux gcc/g++ 出来的⼆进制程序,默认是 release 模式。
  • 要使⽤gdb调试,必须在源代码⽣成⼆进制程序的时候, 加上 -g 选项,如果没有添加,程序⽆法被编译
 gcc a.c -o a.out //releasegcc a.c -o a.out -g //debug\

常⻅使⽤

  • 开始: gdb binFile
  • 退出: ctrl + d 或 quit 调试命令

gdb 调试命令说明

命令作用样例
list/l显示源代码,从上次位置开始,每次列出10行list/l 10
list/l 函数名列出指定函数的源代码list/l main
list/l 文件名:行号列出指定文件的源代码list/l mycmd.c:1
r/run从程序开始连续执行run
n/next单步执行,不进入函数内部,逐过程F10next
s/step单步执行,进入函数内部,逐语句F11step
break/b [文件名:]行号在指定行号设置断点break 10
break test.c:10
break/b 函数名在函数开头设置断点break main
info break/b查看当前所有断点的信息info break
finish执行到当前函数返回,然后停止finish
print/p 表达式打印表达式的值print start+end
p 变量打印指定变量的值p x
set var 变量=值修改变量的值set var i=10
continue/c从当前位置开始连续执行程序continue
delete/d breakpoints删除所有断点delete breakpoints
delete/d breakpoints n删除序号为n的断点delete breakpoints 1
disable breakpoints禁用所有断点disable breakpoints
enable breakpoints启用所有断点enable breakpoints
info/i breakpoints查看当前设置的断点列表info breakpoints
display 变量名跟踪显示指定变量的值(每次停止时)display x
undisplay 编号取消对指定编号的变量的跟踪显示undisplay 1
until 行号执行到指定行until 20
backtrace/bt查看当前执行栈的各级函数调用及参数backtrace
info/i locals查看当前栈帧的局部变量值info locals
quit退出GDB调试器quit

📌 安装cgdb:
• 上⾯的基本调试还是⿇烦,虽然是⿊屏,但是还是想看到代码调试
• 推荐安装cgdb:
• Ubuntu: sudo apt-get install -y cgdb
• Centos: sudo yum install -y cgdb

cgdb下的调试 : 增加断点 查看 断点 删除断点 查看函数 逐过程 逐语句 运行 监视变量 监视内存 等等 仿照VS 自行尝试

结语:

从 gcc 编译的 “四步拆解” 到 gdb 调试的 “断点魔法”,再到 git 版本控制的 “代码托管”,这些工具是 Linux 开发的 “基石套装”。若对编译链接原理、Makefile 语法还存疑,不妨多跑几遍示例代码;想进一步深入,也可以尝试用静态库 / 动态库优化自己的项目。

如果觉得本篇文章对你有所帮助的话 可以点赞加收藏 ,如果还有技术或者疑惑的话 可以在评论区里交流

在这里插入图片描述

http://www.dtcms.com/a/532101.html

相关文章:

  • C语言练习题(二)
  • 【QSS】软件界面的美工操作——Qt 界面优化
  • 网页版html编辑器手机优化大师
  • 【思想比实现更重要】高并发场景下如何保证接口幂等性
  • Spring Expression Language (SpEL) 详解:功能强大的表达式引擎
  • LeetCode:773. 滑动谜题
  • MATLAB基于类别加权灰靶决策的教学评价研究
  • C16可变参数模板函数和普通函数模板
  • 网站建设规划设计方案建设部门电工证查询网站
  • ​​lseek​​的“时空跳跃”:从获取大小到制造“文件空洞”
  • 技术演进中的开发沉思-151 java-servlet:会话管理
  • 【IO多路转接】IO 多路复用之 select:从接口解析到服务器实战
  • 淄博周村学校网站建设定制wordpress文章和页面
  • Multitouch for mac 触控板多点手势创建
  • SIGCHLD:进程终止与僵尸进程清理的关键
  • 数据结构(10)
  • 南皮做网站的团队管理的七个要点
  • Mysql的数据备份和高可用
  • 【Kotlin】数组集合常用扩展函数
  • css新增盒子属性——尺寸调节
  • 做阿里国际网站会有成效吗上海网站建设公司招人
  • 【课堂笔记】概率论-3
  • 【硬件基础篇】:CPU如何被制造出来
  • 面向模块的综合技术之控制集优化(七)
  • 做网站广告软件网站系统设计目标
  • 使用稀疏采样方法减轻汽车雷达干扰——论文阅读
  • 阮一峰《TypeScript 教程》学习笔记——d.ts 类型声明文件
  • Spring AOP:横切关注点的优雅解决方案
  • 如何申请网站空间和注册域名鞋子软文推广300字
  • 基于AutoDL远端服务器在pycharm复现:具身智能论文pai0