
1. 本节目标在正式进入 Linux 开发之前我们需要掌握一系列基础开发工具。本节将带你系统学习以下内容学习 yum 工具进行软件安装掌握 vim 编辑器使用学会 vim 的简单配置掌握 gcc/g 编译器的使用并了解其过程、原理掌握简单的 Makefile 编写了解其运行思想编写自己的第一个 Linux 程序进度条学习 git 命令行的简单操作能够将代码上传到 Github 上掌握简单 gdb 使用用于调试2. 软件包管理器2.1 什么是软件包在 Linux 下安装软件一个通常的办法是下载到程序的源代码并进行编译得到可执行程序。但是这样太麻烦了于是有些人把一些常用的软件提前编译好做成软件包可以理解成 Windows 上的安装程序放在一个服务器上通过包管理器可以很方便的获取到这个编译好的软件包直接进行安装。软件包和软件包管理器就好比「App」和「应用商店」这样的关系。yumYellow dog Updater, Modified是 Linux 下非常常用的一种包管理器主要应用在 Fedora、RedHat、Centos 等发行版上。Ubuntu 主要使用 aptAdvanced Package Tool作为其包管理器apt 同样提供了自动解决依赖关系、下载和安装软件包的功能。2.2 Linux 软件生态Linux 下载软件的过程Ubuntu、Centos、other涉及以下几个关键问题操作系统的好坏评估——生态问题为什么会有免费特定社区提供软件还发布还提供云服务器让你下载软件包依赖的问题国内镜像源以下是一些国内 Linux 软件安装源的官方链接阿里云官方镜像站阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区提供了丰富的 Linux 发行版镜像包括 CentOS、Ubuntu、Debian 等。清华大学开源软件镜像站清华大学开源软件镜像站 | Tsinghua Open Source Mirror提供了多种 Linux 发行版的镜像以及 Python、Perl、Ruby 等编程语言的扩展包。中国科学技术大学开源镜像站USTC Open Source Software Mirror提供了多种 Linux 发行版的镜像以及常用的编程语言和开发工具。北京交通大学自由与开源软件镜像站https://mirror.bjtu.edu.cn/提供了多种 Linux 发行版的镜像以及相关的软件仓库和工具。中国科学院软件研究所镜像站ISCASIndex of /提供了多种 Linux 发行版、编程语言和开发工具的镜像。上海交通大学开源镜像站Index of /提供了丰富的 Linux 软件资源包括多种发行版的镜像和软件仓库。网易开源镜像站欢迎访问网易开源镜像站提供了多种 Linux 发行版的镜像以及相关的软件仓库和工具。此外还有一些其他的国内镜像源如搜狐开源镜像站等但可能由于时间变化或政策调整部分镜像站的链接或状态可能有所变动。建议在使用前访问官方网站或咨询相关社区以获取最新的信息和帮助。sudo yum install -y epel-release2.3 yum 具体操作2.3.1 查看软件包通过 yum list 命令可以罗列出当前一共有哪些软件包。由于包的数量可能非常之多这里我们需要使用 grep 命令只筛选出我们关注的包。例如# Centos $ yum list | grep lrzsz lrzsz.x86_64 0.12.20-36.el7 base Ubuntu $ apt search lrzsz Sorting... Done Full Text Search... Done cutecom/focal 0.30.3-1build1 amd64 Graphical serial terminal, like minicom lrzsz/focal,now 0.12.21-10 amd64 [installed] Tools for zmodem/xmodem/ymodem file transfer $ apt show lrzsz Package: lrzsz Version: 0.12.21-10 Priority: optional Section: universe/comm Origin: Ubuntu Maintainer: Ubuntu Developers ubuntu-devel-discusslists.ubuntu.com Original-Maintainer: Martin A. Godisch godischdebian.org ...注意事项软件包名称主版本号.次版本号.源程序发行号-软件包的发行号.主机平台.cpu架构。x86_64 后缀表示 64 位系统的安装包i686 后缀表示 32 位系统安装包。选择包时要和系统匹配。el7 表示操作系统发行版的版本。el7 表示的是 centos7/redhat7el6 表示 centos6/redhat6。最后一列 base 表示的是软件源的名称类似于小米应用商店、华为应用商店这样的概念。2.3.2 安装软件通过 yum我们可以通过很简单的一条命令完成 gcc 的安装。# Centos $ sudo yum install -y lrzsz Ubuntu $ sudo apt install -y lrzszyum/apt 会自动找到都有哪些软件包需要下载这时候敲 y 确认安装。出现 complete 字样或者中间未出现报错说明安装完成。注意事项安装软件时由于需要向系统目录中写入内容一般需要 sudo 或者切到 root 账户下才能完成。yum/apt 安装软件只能一个装完了再装另一个。正在 yum/apt 安装一个软件的过程中如果再尝试用 yum/apt 安装另外一个软件yum/apt 会报错。如果 yum/apt 报错请自行百度。2.3.3 卸载软件仍然是一条命令# Centos sudo yum remove [-y] lrzsz Ubuntu sudo apt remove [-y] lrzsz2.3.4 注意事项关于 yum/apt 的所有操作必须保证主机虚拟机网络畅通可以通过 ping 指令验证ping www.baidu.com # 当然 yum/apt 也能离线安装但是和我们当前无关暂不关心。2.4 安装源Centos 安装源路径$ ll /etc/yum.repos.d/ total 16 -rw-r--r-- 1 root root 676 Oct 8 20:47 CentOS-Base.repo # 标准源 -rw-r--r-- 1 root root 230 Aug 27 10:31 epel.repo # 扩展源 # 安装扩展源方便课堂演示 # $ sudo yum install -y epel-releaseUbuntu 安装源路径$ cat /etc/apt/sources.list # 标准源 $ ll /etc/apt/sources.list.d/ # 扩展源 # ubuntu 安装扩展源就结合未来具体场景更新安装源现场说明原理即可。云服务器不用考虑因为软件源都是国内的了。3. 编辑器 Vim3.1 Linux 编辑器 - vim 使用vi/vim 的区别简单点来说它们都是多模式编辑器不同的是 vim 是 vi 的升级版本它不仅兼容 vi 的所有指令而且还有一些新的特性在里面。例如语法加亮可视化操作不仅可以在终端运行也可以运行于 x window、mac os、windows。我们课堂上统一按照 vim 来进行讲解。3.2 vim 的基本概念课堂上我们讲解 vim 的三种模式其实有好多模式目前掌握这 3 种即可分别是命令模式command mode、插入模式Insert mode和底行模式last line mode各模式的功能区分如下正常/普通/命令模式Normal mode控制屏幕光标的移动字符、字或行的删除移动复制某区段及进入 Insert mode 下或者到 last line mode。插入模式Insert mode只有在 Insert mode 下才可以做文字输入按「ESC」键可回到命令行模式。该模式是我们后面用的最频繁的编辑模式。末行模式last line mode文件保存或退出也可以进行文件替换找字符串列出行号等操作。在命令模式下shift: 即可进入该模式。要查看你的所有模式打开 vim底行模式直接输入 :help vim-modes。这里一共有 12 种模式six BASIC modes 和 six ADDITIONAL modes。3.3 vim 的基本操作进入 vim在系统提示符号输入 vim 及文件名称后就进入 vim 全屏幕编辑画面$ vim test.c有一点要特别注意就是你进入 vim 之后是处于[正常模式]你要切换到[插入模式]才能够输入文字。[正常模式]切换至[插入模式]输入 a、输入 i、输入 o。[插入模式]切换至[正常模式]目前处于[插入模式]就只能一直输入文字如果发现输错了字想用光标键往回移动将该字删除可以先按一下「ESC」键转到[正常模式]再删除文字。当然也可以直接删除。[正常模式]切换至[末行模式]「shift ;」其实就是输入「:」。退出 vim 及保存文件在[正常模式]下按一下「:」冒号键进入「Last line mode」例如:w保存当前文件、:wq输入「wq」存盘并退出 vim、:q!输入 q!不存盘强制退出 vim。3.4 vim 正常模式命令集插入模式按「i」切换进入插入模式「insert mode」按 i 进入插入模式后是从光标当前位置开始输入文件。按「a」进入插入模式后是从目前光标所在位置的下一个位置开始输入文字。按「o」进入插入模式后是插入新的一行从行首开始输入文字。从插入模式切换为命令模式按「ESC」键。移动光标vim 可以直接用键盘上的光标来上下左右移动但正规的 vim 是用小写英文字母「h」、「j」、「k」、「l」分别控制光标左、下、上、右移一格。按「G」移动到文章的最后。按「$」移动到光标所在行的行尾。按「^」移动到光标所在行的行首。按「w」光标跳到下个字的开头。按「e」光标跳到下个字的字尾。按「b」光标回到上个字的开头。按「#l」光标移到该行的第 # 个位置如5l, 56l。按「gg」进入到文本开始。按「shiftg」进入文本末端。按「ctrl」「b」屏幕往后移动一页。按「ctrl」「f」屏幕往前移动一页。按「ctrl」「u」屏幕往后移动半页。按「ctrl」「d」屏幕往前移动半页。删除文字「x」每按一次删除光标所在位置的一个字符。「#x」例如「6x」表示删除光标所在位置的后面包含自己在内6 个字符。「X」大写的 X每按一次删除光标所在位置的前面一个字符。「#X」例如「20X」表示删除光标所在位置的前面20 个字符。「dd」删除光标所在行。「#dd」从光标所在行开始删除 # 行。复制「yw」将光标所在之处到字尾的字符复制到缓冲区中。「#yw」复制 # 个字到缓冲区。「yy」复制光标所在行到缓冲区。「#yy」例如「6yy」表示拷贝从光标所在的该行往下数6 行文字。「p」将缓冲区内的字符贴到光标所在位置。注意所有与 y 有关的复制命令都必须与 p 配合才能完成复制与粘贴功能。替换「r」替换光标所在处的字符。「R」替换光标所到之处的字符直到按下「ESC」键为止。撤销上一次操作「u」如果您误执行一个命令可以马上按下「u」回到上一个操作。按多次 u 可以执行多次回复。「ctrl r」撤销的恢复。更改「cw」更改光标所在处的字到字尾处。「c#w」例如「c3w」表示更改 3 个字。跳至指定的行「ctrl」「g」列出光标所在行的行号。「#G」例如「15G」表示移动光标至文章的第 15 行行首。3.5 vim 末行模式命令集在使用末行模式之前请记住先按「ESC」键确定您已经处于正常模式再按「:」冒号即可进入末行模式。列出行号「set nu」输入「set nu」后会在文件中的每一行前面列出行号。跳到文件中的某一行「#」「#」号表示一个数字在冒号后输入一个数字再按回车键就会跳到该行了如输入数字 15再回车就会跳到文章的第 15 行。查找字符「/关键字」先按「/」键再输入您想寻找的字符如果第一次找的关键字不是您想要的可以一直按「n」会往后寻找到您要的关键字为止。「?关键字」先按「?」键再输入您想寻找的字符如果第一次找的关键字不是您想要的可以一直按「n」会往前寻找到您要的关键字为止。保存文件「w」在冒号输入字母「w」就可以将文件保存起来。离开 vim「q」按「q」就是退出如果无法离开 vim可以在「q」后跟一个「!」强制离开 vim。「wq」一般建议离开时搭配「w」一起使用这样在退出的时候还可以保存文件。3.6 vim 操作总结vim 的核心在于三种模式的灵活切换正常模式、插入模式和底行模式。我们一共有 12 种总模式大家下来可以研究一下。vim 操作涵盖打开、关闭、查看、查询、插入、删除、替换、撤销、复制等操作。建议通过当堂口头模式切换练习来巩固记忆。3.7 简单 vim 配置了解配置文件的位置在目录 /etc/ 下面有个名为 vimrc 的文件这是系统中公共的 vim 配置文件对所有用户都有效。而在每个用户的主目录下都可以自己建立私有的配置文件命名为.vimrc。例如/root 目录下通常已经存在一个 .vimrc 文件如果不存在则创建之。切换用户成为自己执行 su进入自己的主工作目录执行 cd ~。打开自己目录下的 .vimrc 文件执行 vim .vimrc。常用配置选项用来测试设置语法高亮syntax on显示行号set nu设置缩进的空格数为 4set shiftwidth4使用插件要配置好看的 vim原生的配置可能功能不全可以选择安装插件来完善配置保证用户是你要配置的用户接下来安装 TagList 插件下载 taglist_xx.zip解压完成将解压出来的 doc 的内容放到 ~/.vim/doc将解压出来的 plugin 下的内容拷贝到 ~/.vim/plugin。在 ~/.vimrc 中添加let Tlist_Show_One_File1、let Tlist_Exit_OnlyWindow1、let Tlist_Use_Right_Window1。安装文件浏览器和窗口管理器插件WinManager。下载 winmanager.zip2.X 版本以上的。解压 winmanager.zip将解压出来的 doc 的内容放到 ~/.vim/doc将解压出来的 plugin 下的内容拷贝到 ~/.vim/plugin。在 ~/.vimrc 中添加let g:winManagerWindowLayoutFileExplorer|TagList、nmap wm :WMTogglecr。然后重启 vim打开 ~/XXX.c 或 ~/XXX.cpp在 normal 状态下输入 wm你将看到上图的效果。更具体移步点我其他手册请执行 vimtutor 命令。参考资料Vim 从入门到牛逼vim from zero to hero。4. 编译器 gcc/g4.1 背景知识gcc/g 的编译过程分为四个阶段预处理进行宏替换/去注释/条件编译/头文件展开等编译生成汇编汇编生成机器可识别代码链接生成可执行文件或库文件4.2 gcc 编译选项格式gcc [选项] 要编译的文件 [选项] [目标文件]4.2.1 预处理进行宏替换预处理功能主要包括宏定义、文件包含、条件编译、去注释等。预处理指令是以 # 号开头的代码行。gcc -E hello.c -o hello.i选项 -E 的作用是让 gcc 在预处理结束后停止编译过程。选项 -o 是指目标文件.i 文件为已经过预处理的 C 原始程序。4.2.2 编译生成汇编在这个阶段中gcc 首先要检查代码的规范性、是否有语法错误等以确定代码的实际要做的工作在检查无误后gcc 把代码翻译成汇编语言。用户可以使用 -S 选项来进行查看该选项只进行编译而不进行汇编生成汇编代码。gcc -S hello.i -o hello.s4.2.3 汇编生成机器可识别代码汇编阶段是把编译阶段生成的 .s 文件转成目标文件。读者在此可使用选项 -c 就可看到汇编代码已转化为 .o 的二进制目标代码了。gcc -c hello.s -o hello.o4.2.4 链接生成可执行文件或库文件在成功编译之后就进入了链接阶段。gcc hello.o -o hello4.3 动态链接和静态链接在我们的实际开发中不可能将所有代码放在一个源文件中所以会出现多个源文件而且多个源文件之间不是独立的而会存在多种依赖关系如一个源文件可能要调用另一个源文件中定义的函数但是每个源文件都是独立编译的即每个 *.c 文件会形成一个 *.o 文件为了满足前面说的依赖关系则需要将这些源文件产生的目标文件进行链接从而形成一个可以执行的程序。这个链接的过程就是静态链接。静态链接的缺点很明显浪费空间因为每个可执行程序中对所有需要的目标文件都要有一份副本所以如果多个程序对同一个目标文件都有依赖如多个程序中都调用了 printf() 函数则这多个程序中都含有 printf.o所以同一个目标文件都在内存存在多个副本。更新比较困难因为每当库函数的代码修改了这个时候就需要重新进行编译链接形成可执行程序。但是静态链接的优点就是在可执行程序中已经具备了所有执行程序所需要的任何东西在执行的时候运行速度快。动态链接的出现解决了静态链接中提到的问题。动态链接的基本思想是把程序按照模块拆分成各个相对独立部分在程序运行时才将它们链接在一起形成一个完整的程序而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。动态链接其实远比静态链接要常用得多。比如我们查看下 hello 这个可执行程序依赖的动态库会发现它就用到了一 个 c 动态链接库$ ldd hello linux-vdso.so.1 (0x00007fffeb1ab000) libc.so.6 /lib64/libc.so.6 (0x00007ff776af5000) /lib64/ld-linux-x86-64.so.2 (0x00007ff776ec3000) # ldd 命令用于打印程序或者库文件所依赖的共享库列表。在这里涉及到一 个重要的概念库。我们的 C 程序中并没有定义 printf 的函数实现且在预编译中包含的 stdio.h 中也只有该函数的声明而没有定义函数的实现那么是在哪里实现 printf 函数的呢最后的答案是系统把这些函数实现都被做到名为 libc.so.6 的库文件中去了在没有特别指定时gcc 会到系统默认的搜索路径 /usr/lib 下进行查找也就是链接到 libc.so.6 库函数中去这样就能实现函数 printf 了而这也就是链接的作用。4.4 静态库和动态库静态库是指编译链接时把库文件的代码全部加入到可执行文件中因此生成的文件比较大但在运行时也就不再需要库文件了。其后缀名一般为 .a。动态库与之相反在编译链接时并没有把库文件的代码加入到可执行文件中而是在程序执行时由运行时链接文件加载库这样可以节省系统的开销。动态库一般后缀名为 .so如前所述的 libc.so.6 就是动态库。gcc 在编译时默认使用动态库。完成了链接之后gcc 就可以生成可执行文件了如下所示gcc hello.o -o hello。gcc 默认生成的二进制程序是动态链接的这点可以通过 file 命令验证。注意 1Linux 下动态库 XXX.so静态库 XXX.a。Windows 下动态库 XXX.dll静态库 XXX.lib。注意 2课堂这块需要举例说明纯理论太生涩。网吧/网咖的例子。一般我们的云服务器C/C 的静态库并没有安装可以采用如下方法安装# Centos yum install glibc-static libstdc-static -y # ubuntu apt install glibc-static libstdc-static -y其他问题课堂补充条件编译应用场景为什么非得把语言变成汇编编译器自举4.5 gcc 其他常用选项了解即可-E只激活预处理这个不生成文件你需要把它重定向到一个输出文件里面。-S编译到汇编语言不进行汇编和链接。-c编译到目标代码。-o文件输出到文件。-static此选项对生成的文件采用静态链接。-g生成调试信息。GNU 调试器可利用该信息。-shared此选项将尽量使用动态库所以生成文件比较小但是需要系统由动态库。-O0、-O1、-O2、-O3编译器的优化选项的 4 个级别-O0 表示没有优化-O1 为缺省值-O3 优化级别最高。-w不生成任何警告信息。-Wall生成所有警告信息。5. 自动化构建 - make/Makefile5.1 背景会不会写 makefile从一个侧面说明了 一个人是否具备完成大型工程的能力。一个工程中的源文件不计数其按类型、功能、模块分别放在若干个目录中makefile 定义了一系列的规则来指定哪些文件需要先编译哪些文件需要后编译哪些文件需要重新编译甚至至于进行更复杂的功能操作。makefile 带来的好处就是——自动化编译一旦写好只需要一个 make 命令整个工程完全自动编译极大的提高了软件开发的效率。make 是一个命令工具是一个解释 makefile 中指令的命令工具一般来说大多数的 IDE 都有这个命令比如Delphi 的 makeVisual C 的 nmakeLinux 下 GNU 的 make。可见makefile 都成为了一种在工程方面的编译方法。make 是一条命令makefile 是一个文件两个搭配使用完成项目自动化构建。5.2基本使用实例代码#include stdio.h int main() { printf(hello Makefile!\n); return 0; }Makefile 文件myproc:myproc.c gcc -o myproc myproc.c .PHONY:clean clean: rm -f myproc依赖关系上面的文件 myproc它依赖 myproc.c。依赖方法gcc -o myproc myproc.c就是与之对应的依赖关系。项目清理工程是需要被清理的。像 clean 这种没有被第一个目标文件直接或间接关联那么它后面所定义的命令将不会被自动执行不过我们可以显示要 make 执行。即命令——make clean以此来清除所有的目标文件以便重编译。但是一般我们这种 clean 的目标文件我们将它设置为伪目标用 .PHONY 修饰伪目标的特性是总是被执行的。可以将我们的 hello 目标文件声明成伪目标测试一下。什么叫做总是被执行$ stat XXX File: XXX Size: 987 Blocks: 8 IO Block: 4096 regular file Device: fd01h/64769d Inode: 1321125 Links: 1 Access: (0664/-rw-rw-r--) Uid: ( 1000/ whb) Gid: ( 1000/ whb) Access: 2024-10-25 17:05:30.430619002 0800 Modify: 2024-10-25 17:05:25.940595116 0800 Change: 2024-10-25 17:05:25.940595116 0800文件 内容 属性。Modify内容变更时间更新。Change属性变更时间更新。Access常指的是文件最近一次被访问的时间。在 Linux 的早期版本中每当文件被访问时其 atime 都会更新。但这种机制会导致大量的 IO 操作。具体更新原则不做过多解释。结论.PHONY让 make 忽略源文件和可执行目标文件的 M 时间对比。5.3 推导过程myproc:myproc.o gcc myproc.o -o myproc myproc.o:myproc.s gcc -c myproc.s -o myproc.o myproc.s:myproc.i gcc -S myproc.i -o myproc.s myproc.i:myproc.c gcc -E myproc.c -o myproc.i .PHONY:clean clean: rm -f *.i *.s *.o myproc编译$ make gcc -E myproc.c -o myproc.i gcc -S myproc.i -o myproc.s gcc -c myproc.s -o myproc.o gcc myproc.o -o myprocmake 是如何工作的在默认的方式下也就是我们只输入 make 命令。那么make 会在当前目录下找名字叫 Makefile 或 makefile 的文件。如果找到它会找文件中的第一个目标文件target在上面的例子中他会找到 myproc 这个文件并把这个文件作为最终的目标文件。如果 myproc 文件不存在或是 myproc 所依赖的后面的 .o 文件的文件修改时间要比 myproc 这个文件新那么他就会执行后面所定义的命令来生成 myproc 这个文件。如果 myproc 所依赖的 .o 文件也不存在那么 make 会在当前文件中找目标为 .o 文件的依赖性如果找到则再根据那一个规则生成 .o 文件。这有点像一个堆栈的过程当然你的 C 文件和 H 文件是存在的于是 make 会生成 .o 文件然后再用 .o 文件生成 make 的终极任务也就是执行文件 myproc 了。这就是整个 make 的依赖性make 会一层又一层地去找文件的依赖关系直到最终编译出第一个目标文件。在找寻的过程中如果出现错误比如最后被依赖的文件找不到那么 make 就会直接退出并报错而对于所定义的命令的错误或是编译不成功make 根本不理。make 只管文件的依赖性即如果在我找了依赖关系之后冒号后面的文件还是不在那么对不起我就不工作了。5.4 项目清理工程是需要被清理的。像 clean 这种没有被第一个目标文件直接或间接关联那么它后面所定义的命令将不会被自动执行不过我们可以显示要 make 执行。即命令——make clean以此来清除所有的目标文件以便重编译。但是一般我们这种 clean 的目标文件我们将它设置为伪目标用 .PHONY 修饰伪目标的特性是总是被执行的。可以将我们的 hello 目标文件声明成伪目标测试一下。5.5 特殊符号$表示目标文件。$^表示所有的依赖文件。$表示第一个依赖文件。例如我们可以把上面的 Makefile 简写为myproc:myproc.c gcc -o $ $^ .PHONY:clean clean: rm -f myproc5.6 多文件编译在实际工程中一个项目往往由多个源文件组成。例如我们有 main.c、add.c、sub.c 三个源文件可以这样写 Makefilemain:main.o add.o sub.o gcc -o main main.o add.o sub.o main.o:main.c gcc -c main.c -o main.o add.o:add.c gcc -c add.c -o add.o sub.o:sub.c gcc -c sub.c -o sub.o .PHONY:clean clean: rm -f *.o main这样make 会根据依赖关系自动判断哪些文件需要重新编译哪些文件不需要从而大大提升编译效率。6. 第一个 Linux 程序进度条6.1 引入在正式编写进度条之前我们先来理解几个关键概念缓冲区、回车和换行。缓冲区C 语言中的 printf 输出默认是行缓冲的即遇到换行符 \n 才会刷新缓冲区。如果想让内容立即显示可以使用 fflush(stdout) 强制刷新。回车与换行在 Linux 中\r 表示回车回到行首\n 表示换行移动到下一行。进度条的核心就是利用 \r 不断回到行首重新打印。6.2 倒计时程序我们先写一个简单的倒计时程序理解 \r 的用法#include stdio.h #include unistd.h int main() { int cnt 10; while (cnt) { printf(%2d\r, cnt); fflush(stdout); sleep(1); cnt--; } printf(\n); return 0; }运行效果屏幕上会从 10 倒数到 1数字在原地刷新而不是换行输出。6.3 进度条代码接下来我们实现一个带进度百分比和旋转光标的进度条process.c#include process.h #include string.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 / 1000 int 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); }process.h#pragma once #include stdio.h void process_v1(); void FlushProcess(double total, double current);main.c#include process.h #include stdio.h #include unistd.h 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; }MakefileSRC$(wildcard *.c) OBJ$(SRC:.c.o) BINprocessbar $(BIN):$(OBJ) gcc -o $ $^ %.o:%.c gcc -c $ .PHONY: clean: rm -f $(OBJ) $(BIN)代码说明bar 数组用于存放进度条的填充字符。%-100s 表示左对齐占 100 个字符宽度。label 数组提供旋转光标效果利用取模运算循环显示。usleep 控制刷新速度单位是微秒。6.4 编译运行$ gcc -o processbar processbar.c $ ./processbar运行后可以看到一个动态刷新的进度条从 0% 一直走到 100%。7. git 命令行操作7.1 背景git 是一个分布式版本控制工具用于管理代码的版本历史。我们通过 git 可以将代码上传到 Github 等代码托管平台实现代码的备份、协作和版本管理。7.2 安装 git# Centos sudo yum install -y git # Ubuntu sudo apt install -y git7.3 配置用户信息首次使用需要配置用户名和邮箱这些信息会记录在每次提交中$ git config --global user.name 你的名字 $ git config --global user.email 你的邮箱7.4 创建仓库并提交代码在本地目录初始化仓库$ mkdir myproject $ cd myproject $ git init添加文件到暂存区并提交$ git add . # 添加所有文件到暂存区 $ git commit -m 第一次提交7.5 关联远程仓库并推送在 Github 上创建一个空仓库然后关联本地仓库$ git remote add origin https://github.com/用户名/仓库名.git $ git push -u origin master之后每次修改代码只需要重复 add、commit、push 三步即可$ git add . $ git commit -m 更新说明 $ git push7.6 常用命令总结git status查看当前仓库状态。git log查看提交历史。git pull拉取远程最新代码。git clone克隆远程仓库到本地。git branch查看分支。8. gdb 调试器8.1 背景gdb 是 GNU 调试器用于调试 C/C 程序。使用 gdb 调试时编译需要加上 -g 选项生成调试信息$ gcc -g -o myproc myproc.c8.2 进入 gdb$ gdb myproc8.3 常用调试命令list / l查看源代码。break / b设置断点如 break 10 表示在第 10 行设置断点。run / r运行程序遇到断点会暂停。next / n单步执行不进入函数内部。step / s单步执行进入函数内部。print / p打印变量值如 print i。continue / c继续运行到下一个断点。quit / q退出 gdb。8.4 调试示例$ gdb myproc (gdb) break 10 (gdb) run (gdb) print i (gdb) next (gdb) continue (gdb) quit