Linux工具:软件包管理器yum | 编辑器vim | 编译器gcc/g++ | 调试器gdb | 自动化构建工具make/Makefile | Linux小程序:进度条 | git命令行

Posted 跳动的bit

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux工具:软件包管理器yum | 编辑器vim | 编译器gcc/g++ | 调试器gdb | 自动化构建工具make/Makefile | Linux小程序:进度条 | git命令行相关的知识,希望对你有一定的参考价值。

文章目录

【写在前面】

我们学习 Linux 主要是在上面做开发的 —— 写代码、编译代码、调试代码、自动化构建项目、上传 github/gitee。在这里我们一共会介绍 5 - 6 个工具 —— 软件包管理器 yum、文本编译器 vim、代码编译器 gcc/g++、调试器 gdb、自动化构建项目 make/Makefile、以及 Linux 上的 git 的命令行。

一、Linux软件包管理器yum

💦 什么是软件包

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

💦 如何安装软件

通过 yum 我们可以通过一条很简单的命令安装 lrzsz

sudo yum install -y lrzsz

-y 表示有关于这次安装的问题不用请求我的同意,出现 “complete" 字样,说明安装完成。

⚠ 注意

  • 安装软件时由于需要向系统目录中写入内容,一般需要 sudo 或者切换到 root 帐户下才能完成
  • yum 安装软件只能一个装完了再装另一个,正在 yum 安装一个软件的过程中,如果再尝试 yum 安装另一个软件,yum 会报错
  • 如果 yum 报错,请自行百度

💦 如何卸载软件

仍然是一条命令:

sudo yum remove -y lrzsz

-y 表示有关于这次卸载的问题不用请求我的同意,出现 “complete" 字样,说明卸载完成。

💦 关于rzsz

这个工具用于 windows 机器和远端的 Linux 机器通过 xshell 传输文件,安装完毕之后输入 rz 命令后可以通过拖拽的方式将文件上传。

💦 注意事项

  1. 关于 yum 的所有操作必须保证主机(虚拟机)网络畅通,可以通过 ping 指令验证(当然云服务器的话就可忽略了)

ping www.baidu.com

  1. yum 不能同时跑一台机器

💦 查看软件包

通过 yum list 命令可以罗列出当前一共有哪些软件包,由于包的数目可能非常多,我们可以使用 grep 命令筛选到我们关注的包。

yum list | grep lrzsz

结果如下:

说明:

  • lrzsz.x86_64:软件包的名称和适合的版本
  • 0.12.20-36.el7:el7 表示操作系统发行版本 —— Centos7、RedHat7
  • @base:通常指的是这个软件的提供者是谁;
    epel 指的是它的发布发属于扩展源,对于源,稍后会说明

我们可以筛选出 sl 命令,它就属于扩展源:

这里安装 sl 命令后运行 sl 命令就可以看到小火车了:

Centos 7 好玩的命令:用于练手

yum 源 ❓

在这里要提一句,有很多同学玩的是虚拟机,有可能默认带的 yum 源是不满足需求的(国外的或老版本的),这时就需要手动的去更新 yum 源,yum 要安装软件,那么它是怎么知道要去哪个地方找呢 ???

这里 yum 源是在 etc 路径下:

yum.conf 是 yum 的主配置,这个文件尽量不要变换:

可以看到这里的 yum 源,而我们需要改的是 CentOS-Base.repo:

此时我们 vim CentOS-Base.repo,这里已经配置过了 —— 阿里云的服务

其中可以看到很多链接,说人话就是有了这个配置后,当你 yum 时,它会根据你的配置去找,这里我们也可以去复制链接去浏览器试试(注意这里有些链接可能失效了):

说了这么多,我们就来配置一下(这里可以直接百度 Centos 7 更新 yum 源):

  1. 备份

cd /etc/yum.repos.d/
mkdir repo_bak
mv *.repo repo_bak/

  1. 下载新的 CentOS-Base.repo 到 /etc/yum.repos.d/

wget http://mirrors.aliyun.com/repo/Centos-7.repo

  1. yum chean all 清除缓存,运行 yum makecache 生成新的缓存

yum clean all
yum makecache

  1. 安装 EPEL(Extra Packages for Enterprise Linux) 扩展源

yum install -y epel-release

  1. 再次运行 yum clean all 清除缓存,运行 yum makecache 生成新的缓存,查看启用的 yum 源和所有的 yum 源

yum repolist enabled
yum repolist all

  1. 更新 yum

yum -y update

扩展源 ❓

CentOS-Epel.repo 就是扩展源,它一般默认是没有的,像 Base 这种 yum 源对软件的要求就是稳定、可靠、来源明确,但是这样软件更新的速度就比较慢,所以有很多软件都已经开发出来了,这些软件就放在 Epel 中,这里 Base 就像应用商城,Epel 就像浏览器。

二、Linux开发工具

在此之前,别人问你:

🧓🏿你在什么环境下写代码 ?
🧑🏿Visual Studio 2017。

🧓🏿你在什么环境下调试代码?
🧑🏿Visual Studio 2017。

🧓🏿你在什么环境下编译、链接代码?
🧑🏿Visual Studio 2017。

对于上面这种环境,我们称之为集成开发环境。相比我们在 Linux 中大部分情况下我们所使用的工具都是独立的工具 —— 比如我们写代码用 vim、编译用 gcc/g++、调试用 gdb、维护项目关系用 make/Makefile 等。对于这些工具的安装和卸载这里就不多说了,主要讲解它们的使用。

三、Linux编辑器 —— vim的使用

vi/vim 的区别简单说,它们都是多模式编辑器,不同的是 vim 是 vi 的升级版本,它不仅兼容 vi 的所有指令,而且还有一些新特性。例如语法高亮,可视化操作不仅可以在终端运行,也可以运行于 x window、 mac os、windows,所以这里我们学习 vim。事实上 vim 并不好学,成本较高,那为啥还要学习 vim 呢 ?—— 主要原因有两个:其一是 vim 用的比较广泛,当然其功能也是很强大,大成后可以完全摆脱鼠标的束缚;其二是基本所有的 Linux 机器上都默认带有 vim 的,可以这么说,vim 只要熟练了,其它的文本编译器都不足为惧。vim 和 make/Makefile 使用的熟练度是衡量一个 Linux 程序员水平的一个很重要的东西。

1、vim的基本概念

vim 共有 12 种模式:six BASIC modes 和 six ADDITIONAL modes。其中我们目前只学掌握 3 种即可,分别是命令模式(Command mode)、插入模式(Insert mode)、底行模式(last line mode),各模式的功能如下:

  1. 正常/普通/命令模式(Normal mode)
    控制屏幕光标的移动,字符、字或行的删除,移动复制某区段及进入 Insert mode 下,或者到 last line mode。
  2. 插入模式(Insert mode)
    只有在 Inert mode 下,才可以输入,按【Esc】键可回到命令行模式,该模式是我们后面用的最频繁的编辑模式。
  3. 底行/末行模式(last line mode)
    文件保存或退出,也可以进行文件替换,找字符串,列出行号等操作。在命令模式下,shift + : 即可进入该模式。如若要查看你的所有模式:打开 vim,底行模式直接输入 :help vim-modes。

2、vim的基本操作

  1. 进入 vim,在系统提示符号输入 vim 及文件名后,就进入 vim 编辑器

    $ vim test.c
    需要注意的是,进入 vim 后,默认处于【命令模式】,需要切换到【插入模式】才能编辑

  2. 【命令模式】切换至【插入模式】

    输入 a 或 i 或 o

  3. 【插入模式】切换至【命令模式】

    【Esc】即可转到【命令模式】

  4. 【命令模式】切换至【末行模式】

    【shift + :】

  5. 退出 vim 及保存文件,在【命令模式】下,按【:】进入【末行模式】

    :w (保存当前文件)
    :wq (存盘并退出 vim)
    : q! (不存盘强制退出 vim)


如果你在 vim 时,网络突然断线了,此时 vim 会自动帮你保存的,你可以输入下面的选项,如 R 把它恢复出来,保存并退出,但是每次打开时它都如上图提示。怎么解决呢 ?—— 可以先看下面的选项有无 D,如果有,直接 D,否则复制上图的倒数第二行中引号内的内容,出去 rm 即可。

⚠ 注意


一般底行模式和插入模式的切换需要先转至命令模式才能切换。如果将来你不知道你在哪种模式下,比如【REPLACE 替换模式】,可以直接无脑【Esc】直接回到命令模式。

3、vim正常模式命令集

1. 进入插入模式/底行模式

按【i】切换进入插入模式后,是从光标当前位置开始输入文字
按【a】进入插入模式后,是从目前光标所在位置的下一个位置开始输入文字
按【o】进入插入模式后,是插入新的一行,从行首开始输入文字
按【:】进入底行模式

2. 移动光标

上图中绿色的小方块就是我们光标所在的位置,我们可以使用 ⬆ ⬇ ⬅ ➡ 方向键进行移动定位,在 vim 中与之对应的是 h(左) j(下) k(上) l(右) 键(注意只有小写状态下才生效),一般我们最常用的也是这个,当然你要用也没人拦着你。

hjkl 是按字符为单位移动,有时效率较低,所以 vim 中的 w、b 是按单词为单位进行移动(b 是反向)。

比如有人写了一个又臭又长的代码,我想给它删掉,可以【$】直接定位到行尾,相反【^】可以直接定位到行头。如果有上千行的代码可以【G/shift+g】(shifg+g 就是 G)直接定位到最后一行,相反【gg】可以直接定位到第一行。如果想定位到中间如 666 行,先输入 666 后,再【G/shift+g】。

注意这里不仅仅只有这些移动方式,这里只是最基础光标定位操作,其余的罗列在下面。

为啥 vim 使用 hjkl 移动光标 ❓

这是有一定的历史渊源的,当 Bill Joy 开发 Vi 文本编辑器时,他使用的机器机器是 ADM-3A 终端机,这台机器并没有方向键,所以把 HJKL 键作为方向键。当然现如今我们仍然使用 HJKL 的原因是它的使用效率并不比方向键低,其次还有一个关键原因是买键盘可以不用买 104 键了,也不用买 87 键了,直接买 74 键 —— 嗯,算下来划算了不少,携带又方便。当然并不是这个原因啦,其实在有些场景下必须得使用 HJKL,比如 vim 下的批量注释使用方向键是不行的,所以那些想减少学习成本的同学还是老老实实的练吧。

⚠ 注意以下命令都需要区分大小写,G 命令可以用 shift + g 来代替。

【h/j/k/l】vim 可以直接用键盘上的方向键来上下左右移动,但正规的 vim 是用小写英文字母 h/j/k/l
【$】: 移动到光标所在行的结尾
【^】: 移动到光标所在行的行首
【w】:光标跳到下个字的开头
【e】:光标跳到下个字的结尾
【b】:光标回到上个字的开头
【G/shift+g】:进入文本末端
【gg】:进入文本开始
【# + G/shift+g】:光标移到该行的第 # 个位置
【ctrl+b】:屏幕往后移动一页
【ctrl+f】:屏幕往前移动一页
【ctrl+u】:屏幕往后移动半页
【ctrl+d】:屏幕往前移动半页

3. 复制粘贴

【yw】:将光标所在之处到字尾(单词)的字符复制到缓冲区
【#yw】:复制 # 个字符到缓冲区
【yy】:复制光标所在行到缓冲区
【#yy】:例如 【6yy】表示拷贝从光标所在的该行往下数的 6 行文字到缓冲区
【(#)p】:将缓冲区内的字符贴到光标所在位置,如果要粘贴到 100 份,可在 p 之前指定,比如 100+p,注意所有与 y 相关的复制命令都必须与 p 配合才能完成复制与粘贴功能

4. 删除剪切

注意以下的删除都相当于剪切到缓冲区

【x】:每按一次,删除光标所在位置的一个字符
【#x】:例如,【6x】表示从光标位置开始往后删除 6 个字符
【X】:每按一次,删除光标所在位置的前一个字符
【#X】:例如,【20X】表示删除光标所在位置的前 20 个字符
【dd】:删除光标所在行
【#dd】:从光标所在行开始删除 # 行

5.替换

【r】:替换光标所处的字符
【#r】:例如,【2r】表示从光标处开始往后 2 个字符全部替换成你输入的那个字符
【R】:替换光标所在之处的字符,直到按下【Esc】

6. 撤销

【u】:回到上一步操作,可以支持多次撤销
【ctrl + r】:撤销的回复

7.切换大小写

【shift + ~】:切换大小写


8. 更改(这里用替换即可)

【cw】:更改光标所在的字符到字尾处
【c#w】:例如,【c3w】表示更改 3 个字符

9. 跳转至指定行

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

5、vim末行模式命令集

1.批量化替换

【%s/printf/cout/g】:它会把文本里所有的 printf 替换成 cout

2.保存、退出vim

【w】:保存当前文本
【w!】:强制保存当前文本
【q】:退出 vim
【q!】:强制退出 vim
【wq】:保存退出
【wq!】:保存并强制退出

3.查找

【!man 3 函数名】:查看函数手册,按【q】回到命令模式
【/关键字】:查找字符或字符串,如果第一次找的关键字不是想要的,可以按 n 往后查找
【?关键字】:查找字符或字符串,如果第一次找的关键字不是想要的,可以按 n 往后查找
【#】:查找光标所在在的那个单词,如果不是想要的,按入 shift,然后一直 #

/ 和 ? 的查找有什么区别 ❓

/ 是从文本的开头开始往下查找,? 是从文本结尾开始往上查找。

4.编译执行

【!gcc 文件名】:在底行模式下可以直接进行编译
【!./a.out】:底行模式下执行程序
【!命令】:换言之在底行模式下可以在 ! 后跑任何命令 

5.列出行号

【set nu】:会在文本中的每一行列出行号,注意它只能在当前文件生效,且退出会失效,一劳永逸的方法在下面的 vim 配置就提到

6.分屏

【vs 文件名】:可以在当前窗口下新增一个窗口进行分屏,【ctrl + ww】可以将光标定位到另一个窗口,注意退出 vim 的窗口是光标所在的窗口

6、vim操作总结

  1. 三种模式

    命令模式
    插入模式
    底行模式
    
  2. vim 操作

    打开、关闭、查看、查询、插入、删除、替换、撤销、复制等 
    

实际我们通常在对文本进行操作时,不是死板的只能用命令模式,而是用命令模式 + 插入模式,怎么方便怎么来。

7、简单vim配置

对于 vim 配置其实是个不太好的主题。

💦 配置文件的位置
  1. 在目录 /etc/ 下,有个 .vimrc 的文件,这是系统中公共的 vim 配置文件,对所有用户都有效
  2. 而在每个用户的主目录下,都可以建立自己私有的配置文件,命名为 .vimrc。例如,/root 目录下,通常已经存在一个 .vimrc 文件,如果不存在,则自己创建
  3. 切换用户成为自己执行 su,进入自己的主工作目录执行 cd~
  4. 打开自己目录下的 .vimrc 文件,执行 vim .vimrc
💦 常用配置选项(用来测试)
  1. 设置语法高亮: syntax on
  2. 显示行号: set nu
  3. 设置缩进的空格数为 4: set shiftwidth = 4
  4. 更多可百度 vim 常用配置项



这里的配置是我们自己配的,比较简单,但实际上要配好 vim,是需要下各种各样的插件的,这其中的过程非常麻烦。

💦 使用插件(不推荐)

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

  1. 安装 TagList 插件,下载 taglist_xx.zip ,解压完成,将解压出来的 doc 的内容放到 ~/.vim/doc, 将解压出来的 plugin 下的内容拷贝到 ~/.vim/plugin
  2. 在 ~/.vimrc 中添加:Let Tlist_Show_One_File=1 let Tlist_Exit_OnlyWindow=1 let Tlist_Use_Right_Window=1
  3. 安装文件浏览器和窗口管理器插件:WinManager
  4. 下载 winmanager.zip,2.X 版本以上的
  5. 解压 winmanager.zip,将解压出来的 doc 的内容放到 ~/.vim/doc,将解压出来的 plugin 下的内容拷贝到 ~/.vim/plugin
  6. 在 ~/.vimrc 中添加 let g:winManagerWindowLayout='FileExplorer|TagList n map wm :WMToggle
  7. 然后重启 vim,打开 ~/XXX.c 或 ~XXX.cpp,在normal 状态下输入 “wm”。具体请稳步手把手教你把Vim改装成一个IDE编程环境(图文)
💦 gitee(推荐)

在 gitee 上有一键化安装配置 vim的方案(注意这里介绍的 vimforcpp 只支持 centos7):

  1. 在 gitee 上搜索关键字 vimforcpp
  2. 复制安装链接到我们的 xshell,按提示操作: 链接点击这里
  3. 安装成功,按提示使 vim 配置生效

  4. 这里配置完后,缩进用的是 2 个字符,所以我这里按照自己的习惯改成 4 个

8、vim配置参考资料(github)

Vim从入门到牛逼(vim from zero to hero)

9、sudo提升权限问题

这是在【Linux 权限】 中遗留的一个问题,曾经我们在普通用户的操作中,想使用 sudo 来短期提升权限,却并不能如愿,这是因为默认这个用户是不受信任的。解决方案:

  1. 先 su - 切换成 root,在 root 下操作
  2. vim /etc/sudoers 打开配置文件
    可以看到这里有条两条命令,
    其一是允许 root 用户在任何地方运行任何命令:

    其二是在这个组里的用户可以运行任何命令:
  3. 将普通用户添加到组里,保存并退出,注意这里需要 wq!,即使是 root 用户 wq 也不行,因为它是携保护的文件
  4. 完成

10、PacVim

PacVim 是用于学习 vim 指令的一款游戏:

  1. 安装 PacVim
    这款游戏源码托管在 github 上。

    $ git clone https://github.com/jmoon018/PacVim.git
    $ cd PacVim
    $ sudo make install
    
  2. 启动游戏

    $ pacvim
    $ pacvim [LEVEL_NUMER] [MODE]
    

    默认从头开始玩游戏。如果不想,其中 LEVEL_NUMER 表示关卡号,MODE 表示难度,n 为正常模式、h 为困难模式,例如 pacvim 3 h 表示第 3 关困难模式。

  3. 退出游戏

    【Esc/q】
    

四、Linux编译器 —— gcc/g++的使用

在此之前,我们可以使用命令 gcc -v 、g++ -v 查看 gcc、g++ 有没有安装:

1、背景知识

我们的 C 代码写完,要变成可执行程序需要四个阶段:

  1. 预处理
  2. 编译
  3. 汇编
  4. 链接

为啥不能直接将 C语言转成二进制 ❓

它跟历史有关,早期编程可以认为是电路开关,往后是 0 1 指代,再往后有了汇编语言,在这时计算机就出现了编译器的概念了。比如时间回到 C语言诞生之时,现在有两种方案:要么把 C语言转成汇编,再转二进制;要么把 C语言直接转二进制。在计算机这门学科中永远是站在巨人的肩膀上学习的,把 C语言直接转二进制的成本一定会比把 C语言转成汇编,再转二进制的成本要高的。

注意 gcc 可以编译 C,但不能编译 C++;g++ 可以编译 C、C++。除了 C/C++,Linux 下几乎可以直接在命令行中执行大部分后端语言,比如 python、java 等。

当学完了 vim,那么以后的 C++ 代码、刷题都可以在 Linux 下进行操作,在刷题时,大部分的后端判题的系统都是 Linux,所以有时你会发现在做题时,在 VS 下是对的,提交却是错的,除了测试用例不全面,也有可以代码中包含了 windows 相关内容,如 windows.h 的头文件。

2、gcc如何完成

格式: gcc [选项] 要编译的文件 [选项] [目标文件]

💦 预处理(进行宏替换等)
  1. 预处理功能主要包括头文件展开、条件编译、去掉注释、宏替换等
  2. 预处理指令是以 # 号开头的代码行
  3. 实例:gcc –E test.c –o test.i
    选项 “-E” 该选项的作用是让 gcc 在预处理结束后停止编译过程,选项 “-o” 是指目标文件,“.i” 文件为已经经过预处理的干净的 C 原始程序

💨test.c

💨test.i

💦 编译(生成汇编)
  1. 在这个阶段中 gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言

  2. 用户可以使用 “-S” 选项来进行查看,该选项只进行编译而不进行汇编,其中生成的 “.s” 文件就是汇编语言

  3. 实例: gcc –S test.i -o test.s

💨test.s

虽然我们不学汇编,但至少得大概认识

💦 汇编(生成机器可识别代码)
  1. 汇编阶段是把编译阶段生成的 “.s” 文件转成目标文件
  2. 读者在此可使用选项 “-c” 就可以看到汇编代码已经转换为 “.o” 的二进制目标文件了
  3. 实例: gcc –c test.s -o test.o

💨test.o

💦 链接(生成可执行文件或库文件)
  1. 在成功编译之后就进入了链接阶段
  2. 实例:gcc test.o -o mytest

💨执行 mytest

在本文中重点还要学习链接的过程,也就是下面动态库和静态库的概念。

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

💦 gcc选项记忆

为了方便记忆,我们可以看看键盘的左上角的 Esc 键,它刚好对应我们的 -E、-S、-c 选项;而生成的文件的后缀刚好对应镜象文件的后缀 .iso。

💦 函数库

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

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

💦 静态库和动态库
  1. 静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了,其后缀名一般为 “.a”。
  2. 动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为 “.so”,如前面所述的 libc.so.6 就是动态库。gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件,如后所示。 gcc hello.o –o hello。Linux 下编译好一个程序,默认情况下是属于动态链接形成的可执行程序

    ldd 命令查看 mytest 所依赖的库;file 辨别 mytest 的类型。

📝小故事助理解

一般我们写的程序中,大部分都是动态链接,因为动态链接中不需要把库中的内容过多的拷贝,所以相对而言,它的编译效率较高,这是其一;其二,有时候我们下一些软件,比如 VS2017,它上面会有一些组件 C++、C# 等,这些组件并没有在你的硬盘中,而当你要安装时,它会帮你找到这些组件下载,这样有个好处就是咱需要啥就下啥,而不是直接一堆东西装在机器上,而自己需要使用的组件寥寥无几,所以这里就使用动态库的方式实现。一般在服务器上大部分也都是动态链接,不过有时需要将服务在很多机器上部署,那么单纯的动态链接有可能会出问题,因为动态链接是在程序运行之后才去对应加载到内存中,万一有一个库丢失了,那么程序就挂了。所以有时候一些程序它也会采用静态链接,它的好处就是它不依赖任何的动态库,坏处也很明显,效率低。比如静态链接的大小是动态链接的一百倍,要把静态链接这个程序下下来,只能全部下下来,而动态链接是边下边用。总的来说,它俩各有利弊。

比如你是一个新入学的大一新生,你在写作业,突然有了一个上网的需求,因为环境不熟悉,所以问了学长附近的网吧,随后就去玩了一个钟,接着回来继续写作业,这叫动态链接,它生成的可执行程序只包含你的代码及你要使用外部函数的链接。

后来大三了,要准备找工作了,家里给买了台电脑,当你正在学习时,你想上网,就不用去网吧了,只需要打开自己的电 脑玩个一个钟后又继续学习,也就是说静态链接,并没有和外界产生关联。所以静态链接本质就是将库里的代码拷贝到自己的可执行程序里。

📝小结

动态链接的生成的可执行程序的体积往往比较小,但是它依赖第三方库,有一定的风险,万一网吧被查封了。

静态链接虽然生成的可执行程序的体积较大,但是它不依赖第三方库,你有自己的电脑,网吧被查封了,照样可以玩,就是开学、放寒暑假不方便。

动态链接改静态链接 ❓

注意在新版本的 Linux 系统下一般都不会安装 libc.a,只会安装 libc.so,所以就使用不了 -static,解决方法就是安装 glibc-static

sudo yum install glibc-static


可以看到光是一个简单的程序,静态链接的大小就比动态链接高出 100 倍以上。最后关于动静态库的更多细节在后面的学习中会慢慢摸索。

五、Linux调试器 —— gdb使用

1、背景

这里我们对以下代码不理解,需要调试:

对我们编译好的可执行程序 mytest 进行 gdb:

这是因为 Linux 中默认生成的可执行程序是 release 版本的, 如果需要 debug 版本,就得加上 -g 选项:

Linux 中生成的可执行程序,它的二进制的格式是 elf 的,readelf 可以读取内部的格式:

我们可以使用选项 -S 查看可执行程序的分段情况:

gdb 调试 debug 版本:

  1. 在 C语言阶段我们就了解过了程序的发布方式有两种,debug 模式和 release 模式
  2. Linux gcc/g++ 出来的二进制程序,默认是 release 模式
  3. 要使用 gdb 调试,必须在源代码生成二进制程序的时候,加上 -g 选项

一个项目的开发流程 ❓

注意对于上图中的测试来说严格来说并不是测开,如果产品迭代更新,bug 修复等,严格来说就得重新测试,包括上代产品测试过的,这样成本较高,所以就诞生了很多自动化测试的开发需求,这就是测开,所以一般这里成本较低的一些脚本语言做的,如 python。

2、开始使用

ctrl + d/D 或 quit/q:退出调试
list/l 行号(, 行号):显示 binFile 源代码,显示的是当前行号周围的代码,可以利用另一个行号来指定一段区间
list/l 函数名:列出某个函数的源代码
r 或 run:运行程序
n 或 next:单条执行
s 或 step:进入函数调用
break(b) 行号:在某一行设置断点
break 函数名:在某个函数开头设置断点
info break/i b:查看断点信息
finish:执行到当前函数返回,然后停下等待命令
print(p):打印表达式的值,通过表达式可以修改变量的值或调用函数,类似于 p
p/P 变量:打印变量值
set var:修改变量的值
continue 或 c:从当前位置开始连续而非单步执行程序
delete breakpoints/d 或 d:删除所有断点
delete breakpoints n 或 d n:删除序号为 n 的断点
enable (breakpoints) 序号:启用断点,默认全启
disable 序号:禁用断点,默认全禁
display 变量名:跟踪查看一个变量,每次停下来都显示它的值
undisplay:取消对先前设置的那些变量的跟踪
until X 行号:跳至 X 行
breaktrace 或 bt:查看各级函数调用及参数
info (i) locals:查看当前栈帧局部变量的值
quit:退出 gdb

调试代码(入门) ❗

  1. l + 行号(, 行号),显示的是当前行号周围的代码,按回车继续往下显示,直到全部显示完

  2. b + 行号,打断点

  3. i + b,查看断点

  4. r,运行程序(调试)

  5. n,单步执行,继续执行可按 n/Enter(gdb 可以记录历史执行的命令)

  6. p + 变量,查看变量(监视),当循环往下走时,它不会变化,所以它用于查看一次

  7. display + 变量,跟踪查看一个变量,每次停下来都显示它的值

  8. undisplay + 变量序号,取消对先前设置的那些变量的跟踪

  9. c,循环还没走完,想直接跳出循环到下一个断点处(因为我已经确定这个循环是对的)

  10. 结束调试,使用 ctrl + d 或 quit,退出调试

调试代码(进阶),主要演示剩余的指令 ❗

  1. 设置断点

  2. d n,删除序号为 n 的断点;d,删除所有断点
    注意如果删除断点,下一次再加断点,它的 Num 是递增的,除非把 gdb 退出

  3. 设置了 17 行和 24 行的断点,并 r,接着 n 往下走执行 scanf
    注意空行会直接跳过

  4. 这里我们想进入函数内部,先试用 n
    发现它并没有进入,说明 n 是逐过程执行

  5. 重新调试可以再 r

  6. 这里想进入函数内部可以使用逐语句命令 s

  7. bt,查看调用堆栈

  8. a)现在在 Sum 内的循环里,想跳出循环到 return,Sum 里没断点,不能用 c
    until 行号,跳转到指定行号

    b)现在在 Sum 内的循环里,想跳出到 20 行等待,除了使用 until 外,更规范的的还可以使用 finish,执行到当前函数返回,然后停下等待命令,注意不能 finish main 函数


    c)现在在 Sum 内的循环里,想跳到下一个断点处可以直接 c

  9. disable (序号),断点禁用,默认是全禁;
    enable breakpoints (序号),断点启用,默认是全启

  10. 如果在循环里有 100 次循环,想将循环跑到第 80 次可以 set var

📝 小结

最后想必很多同学会觉得 gdb 相比 VS 的调试成本就高的多,其实那是 gdb 用的不熟练导致的,如果熟练了其实可以说它俩的效率是等价的
实际对于 gdb,在往后的编程中用的不多,不用则已,一用惊人,所以还是得了解的。

3、理解

相信大家对 Visual Studio 2017 已经不陌生了,这里为了方便记忆,可以对应 VS 的操作来进行理解记忆

b 行号 ↔ F9 
r ↔ F5
c ↔ F5
n ↔ F10
s ↔ F11
q ↔ shift + F5

六、Linux项目自动化构建工具 —— make/makefile

在此之前对于一个多文件的项目,它们之间的关系是 VS 帮我们维护处理的,而在 Linux 中需要我们自己来维护处理。

1、背景

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

2、依赖关系

其次我们再了解一下什么是 make,什么是 makefile:

  1. make 是一条命令,它可以帮我们自动化构建项目

  2. makefile 是一个文件,自动化构建项目的过程是它完成的

    这个文件里包含目标文件和原始文件的依赖关系和依赖方法。

3、依赖方法

如上

4、文件清理

  1. 文件除了被创建,也是需要被清理的
  2. 像 clean 这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要 make 执行。即命令 —— “make clean”,以此来清除所有的目标文件,以便重编译
  3. 但是一般我们这种 clean 的目标文件,我们将它设置为伪目标,用 .PHONY 修饰,伪目标的特性是,总是被执行的

5、理解

如下实例

6、实例代码

  1. touch makefile/Makefile
    注意 m 可以大写,但不能写错

  2. vim Makefile 表明依赖关系和依赖方法后 wq
    注意依赖方法也就是第二行是以 Tab 开头,并不能以 4 个空格

  3. make 直接编译代码

  4. vim Makefile 清理文件
    一个项目不仅需要能生成文件,还要能清理文件

  5. make clean 清理文件

  6. vim Makefile 用符号简化
    $^ 表示所有依赖的文件
    $@ 表示目标文件

  7. 多文件项目的构建

    注意生成的 mytest 需要依赖 mytest.c、main.c,而 mytest.h 则已经被它们包含了

📝说明

4 ❓

.PHONY 表示定义伪目标,它的意义是 make clean 时 clean 总是可执行的
这个 clean 没有依赖关系,但有依赖方法

什么叫总是可执行的 ?

其中 mytest 和 clean 都是目标文件,只不过 mytest 没有用 .PHONY 修饰,所以 clean 使用 .PHONY 修饰后就由总是不被执行的转换成总是可执行的。

验证 !

但是我们通常不会把目标可执行程序设置成总是可执行的,因为每次编译它都是有成本的,你只要保证程序是最新的,也就是说当你去 vim 原始文件后,它就可以再 make。

Makefile 里有两个目标文件,make mytest 执行 gcc,make clean 执行 rm,为什么 make 可以默认执行 gcc ?

因为 Makefile 在进行自上向下搜索目标文件时,默认生成第一个可执行程序后就不会往下搜索生成了,也就是说 Makefile 默认只生成一个目标文件。

验证 !


注意一般为了符合习惯,不那么写。

make/makefile 实现 一个 C 文件到可执行程序 ❓

验证 !

7、原理

make 是如何工作的,在默认方式下,也就是我们只输入 make 命令:

  • make 会在当前目录下找名字叫 “Makefile” 或 “makefile” 的文件
  • 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,它会找 “mytest” 文件,并把这个文件作为最终的目标文件
  • 如果 “mytest” 文件不存在,或是它所依赖的的后面的 “mytest.o” 文件的修改时间要比 “mytest” 这个文件新(可以用 touch 测试),那么,它就会执行后面所定义的命令来生成 “mytest” 这个文件
  • 如果 “mytest” 所依赖的 “mytest.o” 文件不存在,那么 make 会在当前文件中找目标为 “mytest.o” 文件的依赖性,如果找到则再根据那一个规则生成 “mytest.0” 文件(类似于堆栈的过程)
  • 当然,你的文件是存在的,于是 make 会生成 mytest.o 文件,然后再用 mytest.o 文件声明,make 的终极任务也就是执行 mytest
  • 这就是整个 make 的依赖性,make 会一层一层的去找文件的依赖关系,直到最终编译出第一个目标文件
  • 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到了,那么 make 会直接退出,并报错,而对于所定义的命令的错误,或是编译失败,make 不会理睬
  • make 只管文件的依赖性,即如果在我找到依赖关系之后,冒号后面的文件不存在,那么对不起,make 就不会工作了

七、Linux的第一个小程序 —— 进度条

1、\\r&&\\n