IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发工具 -> 【Linux】Linux 基础开发工具(yum、vim、gcc/g++、gdb、make/makefile、git) -> 正文阅读

[开发工具]【Linux】Linux 基础开发工具(yum、vim、gcc/g++、gdb、make/makefile、git)

📒博客主页:Morning_Yang丶
🎉欢迎关注🔎点赞👍收藏??留言
📌本文所属专栏: 【Linux】
🙏作者水平有限,如果发现错误,敬请指正!感谢感谢!

一、Linux 软件包管理器 yum

1.1 软件包

  • 在Linux下安装软件, 一个通常的办法是下载到程序的源代码,并进行编译,得到可执行程序。
  • 但是这样太麻烦了,于是有些人把一些常用的软件提前编译好,做成软件包(可以理解成windows上的安装程序)放在一个服务器上,通过包管理器可以很方便的获取到这个编译好的软件包,直接进行安装。
  • 软件包和软件包管理器, 就好比 “App” 和 “应用商店” 这样的关系。
  • yum (Yellow dog Updater, Modified) 是 Linux 下非常常用的一种包管理器(工具)。主要应用在 Fedora, RedHat, Centos 等发行版上。

Linux 下安装软件的方式:

  • 源代码安装
  • rpm 包安装
  • yum 工具(yum 是一条命令)

总结:

  • yum 是什么?
    • 我们在 Linux 中下载资源的一种工具,类似于手机上的应用商店。
  • yum 可以为我们提供什么服务呢?
    • 查找、安装、卸载资源。
    • 解决依赖关系。

我们在手机应用商店上面搜索下载软件,本质是在该公司的后端服务器上搜索下载软件。

思考:那么应用商店如何知道该去哪个服务器上下载软件呢?

  • 这个应用商店在本地一定有相关软件的配置信息(比如软件名和其对应的下载链接)。

  • 所以 yum 工具中一定也有配置文件,指明我们应该去哪里下载资源。

  • sudo yum install -y epel-release 拓展源

    有些服务是非标准的,放在其他库,所以需要拓展源

1.2 搜索 & 安装 & 卸载软件包

查看软件包yum list 命令

通过此命令可以罗列出当前一共有哪些软件包。由于包的数目非常之多,一般配合管道(grep 命令)筛选出我们关注的包。

例如:

$ sudo yum list | grep lrzsz

这个指令的含义是:通过管道把yum检索的结果传递给grep,过滤其中包含lrzsz的软件包。

结果如下:

# 软件包的名称和版本
lrzsz.x86_64                             0.12.20-36.el7                @os 

lrzsz是一个用于文件的上传和下载的工具

  • rz(zmodem file receive)接收到Linux端
  • sz(zmodem file send)发送到windows机器

注意:

  • 第 1 列:软件包名称,主版本号.次版本号.源程序发行号-软件包的发行号.主机平台.cpu架构。

    “x86_64” 后缀表示64位系统的安装包,“i686” 后缀表示32位系统安装包,选择包时要和系统匹配。

  • 第 2 列:“el7” 表示操作系统发行版的版本,“el7” 表示的是 centos7/redhat7。

  • 第 3 列:表示的是 “软件源” 的名称,即该软件包的发布方,类似于 “微信官网”,“华为应用商店” 这样的概念。

安装软件yum install 命令

$ sudo yum install -y sl # 一个好玩的东西,可以在屏幕上跑一个小火车,大家可以去试一试

yum 会自动找到都有哪些软件包需要下载,-y 表示直接安装,中途不需要确认。

注意:

  1. 安装软件时由于需要向系统目录中写入内容,一般需要 sudo 命令或者切到 root 账户下才能完成。
  2. yum 安装软件只能一个装完了再装另一个。在 yum 安装一个软件的过程中,如果再尝试用 yum 安装另外一个软件,yum 会报错。

卸载软件yum remove 命令

$ sudo yum remove -y sl # 卸载sl软件以及它的依赖包

更新 yum 源

  • yum 的配置文件 /etc/yum.conf,这个一般不要去改动。

  • 在 yum 配置路径下,会有 yum 源:

    [yzy@VM-4-4-centos day5]$ ls /etc/yum.repos.d/ # 查看yum源
    CentOS-Base.repo  CentOS-Epel.repo  epel.repo  epel-testing.repo
    
    # CentOS-Base.repo: yum源,要求软件可靠稳定才能放进yum源中,这里的软件版本迭代比较慢
    # CentOS-Epel.repo: 扩展源,比如某个软件新开发的版本,就可以放到epel源中
    

    在 CentOS-Base.repo 文件中可以进行软件源(即软件的下载链接)的配置,当你使用 yum install 安装软件时,就会去对应的服务器上下载软件。

    执行 vim /etc/yum.repos.d/CentOS-Base.repo

    image-20220604191653793


1.3 拓展:rz/sz 工具

功能:这个工具用于本地 windows 机器和远端的 Linux 服务器通过 XShell 传输文件。

安装 rzsz 工具:

$ sudo yum install -y lszrz  # lszrz是要安装的软件名称

安装完毕之后可以通过拖拽的方式将文件上传过去。

用法

  1. rz 命令,上传 windows 中的文件到远端 Linux 服务器中:

    $ rz # 运行该命令会弹出一个文件选择窗口,从本地选择文件上传到服务器,上传到服务器的路径为当前执行rz命令的目录。
    
  2. sz 命令,将选定的文件发送到本地 windows 机器:

    $ sz filename # filename是文件名
    

二、Linux 开发工具

我们在 windows 中编写 C/C++ 程序时,常用的 VS,是一个集成开发环境,包含了很多工具包。

而 Linux 下开发,大部分情况下都是使用的一个一个独立的工具。比如编写代码用 vim,编译代码用 gcc,调试代码用 gdb。

2.1 编辑器 - vim

为什么使用 vim 呢?因为它是所有 Linux 环境下自带的。

vi / vim 的区别简单点来说,它们都是多模式编辑器,不同的是 vim 是 vi 的升级版本,它不仅兼容 vi 的所有指令,而且还有一些新的特性在里面。例如语法加亮,可视化操作不仅可以在终端运行,也可以运行于 mac os、windows。

image-20220604192853281


① vim 的基本概念

vim 的常见三种模式(其实有好多模式,掌握常见这 3 种即可)的功能如下:

  • 正常 / 普通 / 命令模式(Normal mode)

    控制屏幕光标的移动,字符、字或行的删除,移动复制某区段及进入 Insert mode 模式或者 last line mode 模式下。

  • 插入模式(Insert mode)

    只有在 Insert mode 模式下,才可以做文字输入编辑。

  • 底行模式(last line mode)

    文件保存或退出,也可以进行文件替换,找字符串,列出行号等操作。

    如果要查看你的所有模式:打开 vim,底行模式直接输入 :help vim-modes

拓展

安装vimforcpp插件

在使用vim前,可以安装一个vimforcpp插件,方便写C/C++代码。初学时不了安装和配置可以直接复制下面这段指令到命令行中:

curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o ./install.sh && bash ./install.sh

常见错误

在这里插入图片描述
如果上一次编辑时异常退出了,就会产生这样的警告,这是因为异常退出导致.swp文件没有删除,当进入vim时遇到这样的警告,按"d"将.swp文件删除即可。


② vim 的基本操作

刚进入 vim 编辑器时,是处于「正常模式」下的,你要切换到「插入模式」才能够输入文字。

  • 「正常模式」切换至「插入模式」,有 3 种方式:
    • 按「i」进入插入模式后,从当前光标所在位置开始输入文字。
    • 按「a」进入插入模式后,从当前光标所在位置的下一个位置开始输入文字。
    • 按「o」进入插入模式后,插入新的一行,从行首开始输入文字。
  • 「插入模式」切换至「正常模式」
    • 按「ESC」键。
  • 「正常模式」切换至「末行模式」
    • 按下「shift + :」,其实就是输入「 : 」冒号。
  • 退出 vim 操作,在「正常模式」中输入「 : 」冒号进入「末行模式」,然后输入:
    • w(保存当前文件)
    • wq(保存当前文件并退出 vim)
    • q!(不保存强制退出 vim)

image-20220604222506909


③ vim 在命令模式中的命令集 (🌟)

移、删、复、替、撤、更、跳,这是正常模式常用的七个类型的指令。

移动光标

  • vim 可以直接用键盘上的方向键来控制光标上下左右移动,但正规的 vim 是用小写英文字母「h」、「j」、「k」、「l」来分别控制光标向左、下、上、右移一格。
  • 按[gg]:移动到文本的开始
  • 按「shift+g / G」:移动到文章的最后
  • 按「 $ 」:移动到光标所在行的 “行尾”
  • 按「^」:移动到光标所在行的 “行首”
  • 按「w」:光标跳到下个字的开头(以单词为单位)
  • 按「e」:光标跳到下个字的字尾(以单词为单位)
  • 按「b」:光标回到上个字的开头(以单词为单位)
  • 按「#l」:光标移到该行的第 # 个位置,如:5l、56l
  • 按「#G」:光标移动到第 # 行
  • 按「#j / k」光标向下 / 上移动 # 行
  • 按「ctrl」+「b」:光标往上移动一页
  • 按「ctrl」+「f」:光标往下移动一页
  • 按「ctrl」+「u」:up 光标往上移动半页
  • 按「ctrl」+「d」:down 光标往下移动半页

删除文字

  • 「x」:每按一次,删除光标所在位置的一个字符(常用
  • 「#x」:例如,「6x」删除光标后面6个字符(包括光标所在位置)
  • 「shift+x / X」:大写的 X,每按一次,删除光标所在位置的 “前面” 一个字符(不包括光标所在位置)
  • 「#X」:例如,「20X」表示删除光标所在位置的 “前面” 20个字符
  • 「dd」:删除光标所在行(常用
  • 「#dd」:从光标所在行开始删除 # 行

👉 这里的删除本质上都是剪切,可以通过p进行粘贴。

复制文字

  • 「yw」:将光标所在之处到字尾的字符复制到缓冲区中。
  • 「#yw」:复制 # 个字到缓冲区。
  • 「yy」:复制光标所在行到缓冲区。(常用
  • 「#yy」:例如,「6yy」表示拷贝从光标所在的行(包含自己在内) “往下数” 6 行文字。
  • 「p」:将缓冲区内的字符粘贴到光标所在位置。注意:所有与“y”有关的复制命令都必须与“p”配合才能完成复制与粘贴功能。(常用
  • 「#p」:将缓冲区内的字符粘贴 # 份到光标所在位置。
  • yy + p:复制粘贴
  • dd + p:剪切粘贴

替换操作

  • 「r」:替换光标所在处的字符(局部文本替换)。(常用
  • 「R」:替换光标所到之处的字符,直到按下「ESC」键为止(整体文本替换)。

字母大小写转换

  • 「shift+~」:先按下 shift 键,再按下波浪号 ~ 不要停,往后遇到的所有小写字母将被转成大写,所有大写字母将被转成小写。

撤销上一次操作:(常用

  • 「u」:如果您误执行一个命令,可以马上按下「u」,回到上一个操作。按多次 “u” 可以执行多次恢复,相当于Windows下的Ctrl+z
  • 「ctrl + r」: 撤销的恢复,相当于Windows下的Ctrl+y

更改操作

  • 「cw」:更改光标所在处的字到字尾处。
  • 「c#w」:例如,「c3w」表示更改3个字。

跳至指定的行

  • 「ctrl」+「g」列出光标所在行的行号。
  • 「#G」:例如,「15G」表示移动光标至文章的第 15 行行首。

④ vim 在末行模式中的命令集 (🌟)

在使用末行模式之前,请记住先按「ESC」键确定您已经处于正常模式,再输入「 : 」冒号即可进入末行模式。

列出行号

  • 「set nu」:输入「set nu」后,会在文件中的每一行前面列出行号。

跳到文件中的某一行

  • 「#」:# 号表示一个数字,在冒号后输入一个数字,再按回车键就会跳到该行了,如输入数字 15,再回车,就会跳到文章的第 15 行。

查找字符命令

  • :/word:向光标之下寻找一个名称为 word 的字符串,如果第一次找的关键字不是您想要的,可以一直按「n」键向下继续寻找该关键字。(常用
  • :?word:向光标之上寻找一个字符串名称为 word 的字符串,如果第一次找的关键字不是您想要的,可以一直按「n」键向上继续寻找该关键字。

批量化替换字符命令

  • :%s/printf/cout/g(把文中所有 printf 替换成 cout,g --global 表示全局的意思)(常用见下文拓展

查看函数手册命令

  • :!man [选项] [函数名](按 q 退出手册)(常用

保存文件命令

  • 「w」: 在冒号输入字母「w」就可以将文件保存起来。可以跟一个感叹号「 w! 」强制保存。

退出 vim 命令

  • 「q」:按「q」就是退出,如果无法退出 vim,可以跟一个感叹号「 q! 」强制退出 vim。
  • 「wq」:一般建议退出时,搭配「w」一起使用,这样在退出的时候还可以保存文件。(常用

多文件多屏操作

  • 比如:如果我们想把 test.c 文件中的 10 行代码复制 test1.c 文件中,该如何操作呢?

  • :vs test1.c(在 vim 中打开 test1.c 文件,左右分屏)

    image-20220604231224066

  • 分屏模式下 按 ctrl + ww 组合键可以切换文件(w 要按两下)。

跑任何想跑的命令

  • 格式 :!命令(! 表示底行执行 bash 命令),比如:

    :!ls -l
    :!gcc -o test test.c
    

⑤ 拓展(添加/取消多行注释 和 替换命令)(?)

vim 中批量添加 & 删除注释

多行注释:

  1. 进入命令行模式,按ctrl + v进入 visual block模式,然后按j, 或者k选中多行,把需要注释的行标记起来

  2. 再按「shift + i / I 」键(大写字母),进入“Insert” 插入模式,插入注释符,例如//

  3. 最后按「ESC」键,就会全部注释了

取消多行注释:

  1. 进入命令行模式,按ctrl + v进入 visual block模式,按字母l(L)横向选中列的个数,例如 // 需要选中2列

  2. 按字母j,或者k选定要去注释的行,将注释符号//两个斜杠全部覆盖

  3. d/x键就可全部取消注释

替换命令

首先将光标移动至要进行替换的行,按":“进入底行模式,键入s/,后面跟要被替换的字符,再键入”/“,后面跟替换后的字符,最后面再跟一个”/"。
如图:
在这里插入图片描述
这个指令的含义是,将光标所在行的main替换成linux。
替换结果:

image-20220925133854867

此外,还可可以在s前面加"%",表示全文,或"num1,num2"指定行,而在后面加"g"表示当前行所有。

image-20220925134141765

这个语句的含义是,将全文所有的main替换成linux。
替换结果:

image-20220925134225454


如何配置 vim 编辑器(vim 配置成本太高,一般建议直接使用别人配置好的)

  • 配置文件的位置

    • 在目录 /etc/ 下面,有个名为 vimrc 的文件,这是系统中公共的 vim 配置文件,对所有用户都有效。

    • 在每个用户的主目录 ~ 下,都可以自己建立私有的配置文件,命名为:.vimrc

      比如 root 用户的 /root 目录下,通常已经存在一个 .vimrc 文件,如果不存在,则创建之。

    • 切换用户成自己,执行 su ,执行 cd ~ 进入自己的主工作目录。

      然后执行 vim .vimrc 打开自己目录下的 .vimrc 文件。添加配置选项即可。

  • 常见的一些配置选项

    • 设置语法高亮:syntax on
    • 显示行号:set nu
    • 设置 tab 缩进的空格数为 4:set shiftwidth=4
  • 使用插件配置 vim

    要配置好看的 vim,原生的配置可能功能不全,可以选择安装插件来完善配置,需要保证用户是你要配置的用户。


2.2 编译器 - gcc/g++

① gcc/g++ 命令 & 程序编译

C/C++程序要运行,一般要经历一下步骤:

预处理 --> 编译 --> 汇编 --> 链接

image-20220526095358736

Linux 下通过 gcc 命令完成 C 程序编译的过程,通过 g++ 命令完成 C++ 程序编译的过程:

  • gcc 命令格式gcc [选项] 要编译的文件 [选项] [目标文件] (g++ 类似)

    • -E 只激活预处理,不生成文件,你需要把输出内容重定向到一个 .i 输出文件里面。
    • -S 只进行预处理、编译阶段,并生成 .s 汇编文件,不进行汇编和链接。
    • -c 只进行预处理、编译、汇编阶段,并生成 .o 目标文件,不进行链接。
    • -o 指明要生成的文件,输出内容到一个输出文件中。
    • -static 此选项对生成的文件采用静态链接。
    • -g 生成调试信息。GNU 调试器可利用该信息。
    • -shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统提供动态库。
    • -O0、-O1、-O2、-O3 编译器的优化选项的 4 个级别,-O0 表示没有优化,-O1 为缺省值,-O3 优化级别最高。
    • -w 不生成任何警告信息。
    • -Wall 生成所有警告信息。
  • 预处理

    命令格式:gcc –E test.c –o test.i

    • 选项 “-o”,是指目标文件,.i 文件为已经过预处理的 C 原始程序。

    预处理阶段做的事:头文件展开、宏替换、条件编译、去掉注释等等。

    预处理指令是以 # 号开头的代码行。

  • 编译(生成汇编)

    命令格式:gcc –S test.i –o test.s

    编译阶段做的事:语法检查,函数实例化,生成 .s 汇编文件。

  • 汇编(生成目标文件)

    命令格式:gcc –c test.s –o test.o

    汇编阶段做的事:把编译阶段生成的 .s 汇编文件转成 .o 目标文件(二进制机器码)

    二进制机器码用vim打开会看到乱码。

  • 链接(生成可执行文件)

    链接阶段会生成可执行文件或库文件,即将若干个二进制代码文件或库文件链接起来生成可执行程序

    命令格式:gcc test.o –o mytest

总结C/C++程序编译的四个步骤:

在这里插入图片描述


② 拓展:函数库

什么是函数库:

在我们的 C 程序中,并没有定义 printf 的函数实现,且在预编译中包含的 “stdio.h” 中也只有该函数的声明,而没有定义函数的实现,那么是在哪里找到 printf 函数的实现的呢?

回答:系统把这些函数实现都被放到名为 libc.so.6 的库文件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径 “/usr/lib” 下进行查找,也就是链接到 libc.so.6 库文件中去,这样就能找到 printf 函数的实现了,而这也就是链接的作用。

函数库的分类:一般分为动态库和静态库

静态库(.a):指编译链接时,把库文件中的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。

动态库(.so):与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。

  • 上述说的 libc.so.6 就是动态库。
  • gcc 在链接阶段默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件。

③ 拓展:动态 & 静态链接

生成可执行程序的方式有两种:

  • 动态链接:链接动态库

    • 优点:不需要把相关库中的代码拷贝到可执行程序中,编译效率高,程序运行起来后,需要用到哪个库,再把哪个库加载到内存中,边运行边加载,省空间(磁盘的空间,内存的空间),加载到内存的时候较小,可执行程序体积小加载速度快
    • 缺点:万一有库丢失了,将直接导致程序无法正常运行,依赖静态库程序可移植性差
  • 静态链接:链接静态库

    • 优点:不依赖于任何的动态库,自己就可以独立运行,程序的运行可移植性好
    • 缺点:占磁盘空间占内存,把相关库中的代码完完全全拷贝到了可执行程序中。

Linux 下生成的可执行程序,默认是动态链接的,如何查看呢?

  • 使用 ldd [filename] 命令可查看可执行文件的库依赖关系。
  • 使用 file [filename] 命令可以查看可执行文件的信息和类型。

gcc编译时默认使用动态库,可以通过file指令查看文件属性,并通过ldd指令查看可执行程序连接的库文件,如图:

image-20220925145613174

从上图可以看出gcc编译使用的动态链接(dynamically linked),并看出链接的库文件。如果要进行静态编译,需要加上-static,静态链接(statically linked),静态链接的时候找不到库说明没有 libc.a文件,需要安装


2.3 调试器 - gdb

① 基本概念

  • 程序的发布方式有两种:

    • debug 模式(在生成可执行程序的时候,会加入调试信息,可调试)
    • release 模式(没有调试信息,不可被调试)
  • Linux 中 gcc/g++ 编译生成的可执行程序,默认是 release 模式

  • 注意:如果要使用调试器 gdb 调试,在源代码生成可执行程序的时,必须加上 -g 选项,生成调试信息。

    $ gcc test.c -o test_debug -g
    

    debug 版本的可执行程序包含有调试信息,文件体积也更大


② gdb 的基本操作

gdb 命令格式:gdb [目标文件名]

这些基本操作中,常用操作都在下方举了例子,例子也是大致按照调试步骤来排列的。

源码显示

  • [list/l + 行号]:显示程序源代码,接着上次的位置往下列,每次列 10 行。

    (gdb) list 1  # 从第一行代码开始显示。接着按回车键,会继续往下显示代码,每次列10行。
    
  • [list/l + 函数名]:列出某个函数的源代码。(比如:l main 列出 main 函数的代码)

  • [l -] 展示上面10行的源代码

运行程序

  • r 或 run:运行程序。(断点打完了,我该如何让程序执行起来,开始调试呢?)(相当于VS中的F5)

    (gdb) r
    Starting program: /home/ll/xxxx/test_debug 
    
    Breakpoint 1, main () at test.c:7  # 此时来到了第一个断点处(第7行)
    7	  for (; i < 10; i++) {
    

    问题:此时我想跳到第二个断点去调试程序,该怎么办呢?

  • continue / c:从当前位置开始连续而非单步执行程序。(跳到下一个断点处)

    (gdb) continue
    Continuing.
    
    Breakpoint 2, main () at test.c:10 # 此时来到了第二个断点处(第10行)
    10	  printf("sum = %d\n", sum);
    
  • n 或 next:单条执行。(逐过程,不会进入函数,相当于VS中的F10)

    (gdb) n
    8	    sum += i; # 单步执行,此时来到了第8行
    
  • s 或 step:进入函数调用。(逐语句,会进入函数,相当于VS中的F11)

  • until X(行号):跳转到第 X 行

  • finish:执行到当前函数返回,然后停下来等待命令(输入 finish 可直接结束当前函数调用,用此命令可以快速判断一个程序中到底是哪个函数出了 bug)(此命令在非 main 函数中才有效)

    (gdb) finish 
    Run till exit from #0  Sum (n=10) at test.c:4
    0x0000000000400609 in main () at test.c:18
    18	  int sum = Sum(n);   # 结束Sum函数调用,直接返回函数结果
    Value returned is $1 = 55 # Sum函数的返回值
    

断点

  • [b / break + 行号]:在某一行设置断点。

    (gdb) b 7
    Breakpoint 1 at 0x400543: file test.c, line 7. # 断点1位于0x400543:文件test.c,第7行
    
  • [break 函数名]:在某个函数开头设置断点。(实际上是这个断点是打在该函数的第一行)

  • [info/i b]:查看断点信息。当前设置了哪些断点。

    image-20220925150920307

    (gdb) info b
    Num     Type           Disp Enb Address            What
    1       breakpoint     keep y   0x0000000000400543 in main at test.c:7  # 第7行
    2       breakpoint     keep y   0x0000000000400555 in main at test.c:10 # 第10行
    
  • [delete breakpoint]:删除所有断点

  • [delete num]:删除序号为 num 的断点

  • [disable num]:禁用断点

  • [enable num]:启用断点

查看变量

  • p + 变量:打印变量值。

    (gdb) p i
    $1 = 3
    (gdb) p sum
    $2 = 3
    (gdb) p &i
    $3 = (int *) 0x7fffffffe44c
    (gdb) p &sum
    $4 = (int *) 0x7fffffffe448
    

    问题:我想要这些变量值随着调试的进行而不断变化,该怎么办呢?

  • display + 变量名:设置常显示。跟踪查看一个变量,每次停下来都显示它的值。

    (gdb) display i   # 跟踪查看变量i
    1: i = 3
    (gdb) display sum # 跟踪查看变量sum
    2: sum = 3
    (gdb) n    # 单步调试
    7	  for (; i < 10; i++) {
    2: sum = 6 # 显示变量i的值,前面的2:是它的序号
    1: i = 3   # 显示变量sum的值,前面的1:是它的序号
    
  • undisplay + 序号:取消常显示。取消对先前设置的那些变量的跟踪。

    (gdb) undisplay 2
    (gdb) undisplay 1
    

堆栈及函数调用

  • bt / breaktrace:查看各级函数调用及参数,即反应函数之间的调用关系(相当于VS中的查看调用堆栈)

    (gdb) bt     # 查看调用堆栈,main函数上面压的是SUm函数
    #0  Sum (n=10) at test.c:4
    #1  0x0000000000400609 in main () at test.c:18
    
  • info(i) locals:查看当前栈帧局部变量的值,即当前函数的帧栈内部

  • q / quit:退出 gdb

Tips

gdb调试时,若无指令输入时按回车,会执行上一次的指令,在进行逐步调试时可以用到,第一次键入n或者s,后面按回车键即可。

注意

调试程序完毕,或者调试中间过程,调试痕迹 很重要,当调试一个大的项目,可能有几十个断点,某个断点调试完了,不要直接把它删除,而是暂时禁用它,等程序没有任何问题了,再把所有断点删除。

2.4 项目自动化构建工具 - make/Makefile

① 基本概念

背景:

  • 对于一个多文件的项目,在 VS 集成开发环境中,可以自动帮我们维护好多文件,我们只需要一键就可以完成对所有文件的编译,生成可执行程序。
  • 而在 Linux,项目的所有文件,都需要我们自己来维护,成本太高,所以要用到 make 和 Makefile 帮我们自动化维护。

Linux 中项目自动化构建工具 - make/makefile:

  • make 是一条命令,makefile 是一个文件(文件中保存的是 目标文件原始文件 间的依赖关系和依赖方法),两个搭配使用,完成项目自动化构建。
  • 一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile 定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。
  • makefile 带来的好处就是 —— “自动化编译”,一旦写好,只需要一个 make 命令,整个工程完全自动编译,极大的提高了软件开发的效率。
  • make 是一个命令工具,是一个解释 makefile 中指令的命令工具,一般来说,大多数的 IDE 都有这个命令,比如:Delphi 的 make,Visual C++ 的 nmake,Linux 下 GNU 的 make。可见,makefile 都成为了一种在工程方面的编译方法。

② 基本使用

假如我现在编写了一个 test.c 文件,需要编译文件生成可执行程序:

方式一:直接使用 gcc 命令:

$ gcc test.c -o test

方式二:可以用 make 命令:想要使用 make 命令,需要创建一个 makefile 文件,如何创建 makefile 文件呢?

首要先了解以下知识:

依赖关系和依赖方法:

  • 依赖关系表明我依赖于谁
  • 依赖方法指的是对应的那个方法如何生成我

比如上述例子,单文件项目,只有 test.c 一个文件

  • 目标文件 test 依赖于原始文件 test.c,但光有依赖关系是不能生成目标文件的,是不够的。

  • 还需要有依赖方法,而 gcc test.c -o test 就是与之对应的依赖方法,表明如何生成目标文件 test。

  • image-20220606134720425

编写 Makefile 文件

  • Makefile是文件,其内部定义编译程序的规则文件格式为:

     目标对象:依赖对象
     	编译命令
    

    注意第二行编译命令的前面有一个Tap键的空间(四个空格)。

    test:test.c            # 表明了一种依赖关系,目标文件 test 依赖于 test.c         
    	gcc test.c -o test # 依赖方法,怎么用 test.c 生成目标文件 test(需要以tab键开头)
    .PHONY:clean           # .PHONY —— "定义"伪目标:clean总是可以被执行的
    clean:                 # 依赖项为空
    	rm -rf test        # 清理可执行程序
    

编写完 makefile 文件后,使用 make 命令

image-20220925154715147

解释:.PHONY 的作用

.PHONY:[伪目标名称]
伪目标的特性:命令总是被执行,不关心最后一次修改时间

一般不会把可执行程序 “定义” 成伪目标,因为每次编译都是有成本的,第一次编译好了,就不需要再编译了,除非文件有改动。一般把清理可执行程序 “定义” 成伪目标。

根据伪目标的特性,可以进行项目清理,清除所有目标文件,以便重新编译。

  • 简化 makefile 文件:

    • $^ 所有的依赖对象
    • $@ 目标对象
    • $< 第一个依赖对象
    test:test.c # 可以有多个依赖对象
    	gcc -o $^ $@  # $^: 可执行程序所依赖的文件列表 $@: 目标文件
    .PHONY:clean
    clean:
    	rm -rf test
    

    因为依赖对象可以由多个,所以这里可以由多个源文件生成一个可执行程序,这便是makefile的内部变量的意义。

多文件项目,有 test.h test.c main.c 三个文件

  • 编写 makefile 文件:

    test:test.c main.c  # 目标文件 test 依赖于 test.c 和 main.c
    	gcc $^ -o $@    # $^: 可执行程序所依赖的文件列表 $@: 目标文件
    .PHONY:clean
    clean:
    	rm -rf test
    

思考:为什么没有把 .h 头文件加入进来呢?

编译代码时头文件会展开,把头文件中的代码拷贝到源文件中,所以找到头文件才是最重要的,找头文件通常有两种路径:当前路径、系统路径。

总结

  • 以后当遇到的项目变复杂了,文件多了,不用直接写 gcc 命令了,而是用 make/makefile 自动化构建项目。

③ 原理

make 的推导过程图:

image-20220606140803169

make 是如何工作的:

  1. make 会在当前目录下找名字叫 “Makefile” 或 “makefile” 的文件。
  2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到 “test” 这个文件,并把这个文件作为最终的目标文件。
  3. 如果 test 文件不存在,或是 test 所依赖的后面的 test.o 文件的文件修改时间要比 test 文件新,那么就会执行后面所定义的命令来生成 test 这个文件。
  4. 如果 test 所依赖的 test.o 文件不存在,那么 make 会在当前文件中找目标文件为 test.o 的依赖性,如果找到则再根据那一个规则生成 test.o 文件。
  5. 这就是整个 make 的依赖性,make 会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
  6. 在寻找的过程中,如果出现错误,比如最后被依赖的文件找不到,那么 make 会直接退出并报错(因为 make 只管文件的依赖性,不会管依赖的文件项到底在不在)。如果是所定义的命令的错误,或是程序编译不成功,make 不会理会。

clean 项目清理:

  • 工程是需要被清理的。
  • 比如 clean,如果没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要 make 执行。即命令 make clean,以此来清除所有的目标文件,以便重新编译。
  • 一般会把 clean 设置为伪目标,用 .PHONY 修饰。(伪目标的特性是:总是被执行的)

2.5 分布式版本控制软件 - git

git 是一个分布式版本控制软件,最初由林纳斯·托瓦兹创作,于2005年以GPL许可协议发布。最初目的是为了更好地管理Linux内核开发而设计。

安装 git:

$ sudo yum install -y git

1、在 gitee/github 创建新仓库:略,然后在创建好的仓库页面中复制远程仓库的地址 url,推荐 HTTPS。

2、克隆远程仓库到本地:

$ git clone [url]

执行命令后,输入 gitee/github 的用户名和密码,将会直接在本地创建一个放置代码的目录。

注意:不要修改隐藏文件 .git 里面的内容

3、把需要提交的代码拷贝到本地仓库中

4、三板斧(提交本地仓库文件到远程仓库)

  • git add [文件名 ] 添加文件
  • git commit [文件名] -m “备注信息” 提交改动到本地
$ git add .                     # 添加所有文件到暂存区
$ git commit -m "first commit"  # 提交文件到本地仓库,""中写提交信息,不能乱写
$ git push        # 推动本地仓库的文件到远程仓库

执行 git commit -m “msg” 时,会让你设置用户名和邮箱(每次 git 提交都会使用到该信息,它被永远的嵌入到了你的提交中):

$ git config --global user.email "you@example.com" # 邮箱
$ git config --global user.name "Your Name"        # 用户名

当依次执行三个命令后,输入 gitee/github 的用户名和密码,在 gitee/github 上的远程仓库刷新就可以看到自己的代码啦。

5、配置免密码提交,参考文章:git本地免密码和账号

6、补充:

$ git log    # 查看所有提交日志信息
$ git status # 查看本地仓库所有文件状态

三、实战小程序

回车与换行的区别:

3.1 行缓冲区概念

1)这段代码在 Linux 中运行,会是什么效果呢?

#include<stdio.h>
#include<unistd.h> // sleep()                                                     
int main()
{
	printf("hello world\n"); // 有'\n'
	sleep(5);
    return 0;
}

运行结果:先打印出 hello world ,然后休眠 5s,结束程序。

2)这段代码在 Linux 中运行,会是什么效果呢?

#include<stdio.h>
#include<unistd.h> // sleep()                                                     
int main()
{
	printf("hello world"); // 没有'\n'
	sleep(5);
    return 0;
}

运行结果:先休眠了 5s,当 5s 结束后,才打印出 hello world ,结束程序。

思考】:

sleep(5); 执行的时候,printf("hello world"); 已经执行完了,但是却没有先打印字符串,这是为什么呢?

  • printf("hello world"); 已经执行完了,但是并不代表字符串就得显示出来。
  • 那在执行 sleep(5); 期间,字符串在哪里呢?-- 缓冲区本质就是一段内存空间,可以暂存临时数据,在合适的时候刷新出去)。

拓展】:

刷新是什么?-- 把数据真正的写入磁盘、文件、显示器、网络等设备或文件中。

刷新策略:

  1. 直接刷新,不缓冲。
  2. 缓冲区写满,再刷新(称为全缓冲)。
  3. 碰到 ‘\n’ 就刷新,称为行刷新。(注:行刷新一般对应的设备是显示器)
  4. 强制刷新。

任何一个 C 程序,启动的时候,都会默认打开三个流(文件):

  • 标准输入 stdin、标准输出 stdout、错误 stderr(类型是 FILE* 文件指针类型)
  • 如果想要让数据在显示器上显示出来,需要向输出流 stdout 中写入数据。

分析】:

回到上面的问题,为什么在执行 sleep 的时候,没有显示字符串呢?

因为我们想要把字符串显示到显示器上,显示器默认是行刷新,遇到 ‘\n’ 才刷新,而我们上面写的代码中,没有 ‘\n’,所以 printf 执行完了没有刷新。

为了在 printf 执行完的时候,让字符串立马显示出来,需要进行强制刷新,把字符串尽快的写入显示器中。

强制刷新需要用到一个函数:

#include <stdio.h>
int fflush(FILE *stream); // 把当前缓冲区的数据写入到流中

因为是让字符串在显示器上显示,所以我们需要传文件指针 FILE* stdout,代码如下:

#include<stdio.h>
#include<unistd.h> // sleep()                                                     
int main()
{
	printf("hello world"); // 没有'\n',字符串写入到了缓冲区中,但不会被立即刷新出来
    fflush(stdout);        // 强制刷新,把当前缓冲区中的数据写入到输出流文件中
	sleep(5);
    return 0;
}

运行结果:先打印出 hello world ,然后休眠 5s,结束程序。


3.2 什么是:回车 & 换行 & 回车换行

  • 回车:用 ‘\r’ 表示。回到当前行的最开始,如果此时写入数据,会依次往后覆盖掉当前行的数据。
  • 换行
  • 回车换行:光标移动到下一行的最开始

注意:

  • C 语言中的 ‘\n’ 表示:回车并换行
  • 键盘上的 Enter 键表示:回车并换行

图示:

image-20220606174041225


3.3 小程序:倒计时

搞懂了回车的概念,就可以写一个倒计时的小程序了:

/* countDown.c */
#include<stdio.h>  // fflush
#include<unistd.h> // sleep()

int main()
{
    int count = 9;
    while (count >= 0) {
        printf("%-2d\r", count); // 数据写入缓冲区中,\r表示回车,从当前行的最开始写入
        fflush(stdout);          // 强制刷新,把用户缓冲区的数据刷新出来
        count--;
        
        sleep(1); // 为了能够看到倒计时,休眠1s
    }
	return 0;
}

拓展

  • 虽然我们这里 count 是整型,但实际上打印到显示器上,是一个一个的字符。

  • 比如 int count = 123456,占 4 字节,使用 printf 打印到显示器上,是 6 个字符,占 6 字节。

    printf 格式化输出,实际上就是把这个内存级的整型数据转换成显示器可以显示的字符型的数据。

    scanf 格式化输入,实际上就是把键盘敲下的一个个字符型的数据转换成了一个内存级的整型数据。

  • 文件分为二进制文件和文本文件,二进制文件在内存中是什么样子,写到文件中也就是什么样子,而文本文件写入到设备(文件)中时,是需要做转换的,比如显示器设备(也是一种文件),显示器是给人看的,所以它一定不是二进制文件,而是文本文件,只要是文本文件,必须要将所要显示的数据转换成人所能识别的一个一个的字符型数据。

  • 所以键盘和显示器设备(文件),统称为字符设备,体现在输入时是字符,输出时是字符。

3.4 小程序:进度条

void procbar()
{
    int i = 0;
    char proc[102];
    char proc1[] = "|/-\\";
    memset(proc,'\0',sizeof(proc));

    while(i<=100)
    {
        printf("[%-100s][%3d%%][%c]\r",proc, i, proc1[i%4]);
        fflush(stdout);                                                       
        proc[i] = '#';
        usleep(100000);
        i++;
    }
    printf("\n");
}

运行结果:

proc

  开发工具 最新文章
Postman接口测试之Mock快速入门
ASCII码空格替换查表_最全ASCII码对照表0-2
如何使用 ssh 建立 socks 代理
Typora配合PicGo阿里云图床配置
SoapUI、Jmeter、Postman三种接口测试工具的
github用相对路径显示图片_GitHub 中 readm
Windows编译g2o及其g2o viewer
解决jupyter notebook无法连接/ jupyter连接
Git恢复到之前版本
VScode常用快捷键
上一篇文章      下一篇文章      查看所有文章
加:2022-10-17 12:55:18  更:2022-10-17 12:57:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/28 3:50:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计