【Linux】系统开发工具——开发工具(vim,gcc/g++,make/makefile,Linux第一个系统程序:进度条)
一.Linux系统中安装软件
方法:
1.源码安装
2.软件包安装---rpm/deb
3.包管理器yum(centos) apt/apt-get(ubuntu)(推荐)
a.网络下载
b.安装(拷贝)只能使用root权限进行
软件包管理器
什么是软件包?
• 在Linux下安装软件, ⼀个通常的办法是下载到程序的源代码, 并进⾏编译, 得到可执⾏程序.
• 但是这样太⿇烦了, 于是有些⼈把⼀些常⽤的软件提前编译好, 做成软件包(可以理解成windows上的安装程序)放在⼀个服务器上, 通过包管理器可以很⽅便的获取到这个编译好的软件包, 直接进⾏安装.
• 软件包和软件包管理器, 就好⽐ "App" 和 "应⽤商店" 这样的关系.
• yum(Yellow dog Updater, Modified)是Linux下⾮常常⽤的⼀种包管理器. 主要应⽤在Fedora,
RedHat, Centos等发⾏版上.
• Ubuntu:主要使⽤apt(Advanced Package Tool)作为其包管理器。apt同样提供了⾃动解决依赖关系、下载和安装软件包的功能。
二.Linux的生态
1.Linux下载软件的过程

2.如何去评估一个操作系统的好坏?(生态评估)
包含以下几个方面:

3.为什么会有⼈免费特定社区提供软件,还发布?还提供云服务器让你下载?(图解)

开源本质就是一种商业模式
4.软件包依赖的问题
5.镜像

定义
镜像:指一个物体、图像、数据或系统,以对称轴 / 对称面为基准,形成左右相反、结构完全一致的复刻副本,内容与原对象完全相同,仅空间方向反转。
镜像站点:完全同步主站数据的备用服务器,用于分流、加速访问、容灾备份。
大白话就是现在的很多云服务都是外国的社区发明的国内上基本用不了,但其中不乏有一些国内的成员在里面,就想办法通过一些方式让国内也可以用,这种方式就叫做镜像
三.apt的具体操作
这里面是ubuntu系列所以是apt,centos是yum操作
1. 查看软件包
通过 yum list 命令可以罗列出当前⼀共有哪些软件包. 由于包的数⽬可能⾮常之多, 这⾥我们需要使⽤grep 命令只筛选出我们关注的包.
2. 安装软件
通过 apt/yum, 我们可以通过很简单的⼀条命令完成 gcc 的安装.
#Centos
sudo yum install -y lrzsz
#Ubuntu
sudo apt install -y lrzsz
备注:yum/apt 会⾃动找到都有哪些软件包需要下载, 这时候敲 "y" 确认安装. 出现 "complete" 字样或者中间未出现报错, 说明安装完成.
注意事项:
• 安装软件时由于需要向系统⽬录中写⼊内容, ⼀般需要 sudo 或者切到 root 账⼾下才能完成.
• yum/apt安装软件只能⼀个装完了再装另⼀个. 正在yum/apt安装⼀个软件的过程中, 如果再尝试⽤yum/apt安装另外⼀个软件, yum/apt会报错.安装一次任何人都可以使用
3 卸载软件
# Centos
sudo yum remove lrzsz
# Ubuntu
sudo apt remove lrzsz
注意事项:
关于 yum / apt 的所有操作必须保证主机(虚拟机)⽹络畅通
可以通过 ping 指令验证:ping www.baidu.com
当天yum / apt也能离线安装
4 安装源
Cetnos 安装源路径:
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-release
Ubuntu 安装源路径:
cat /etc/apt/sources.list # 标准源
ll /etc/apt/sources.list.d/ # 扩展源
# ubuntu 安装扩展源,就结合未来具体场景
ubuntu apt更换国内镜像源
【文档参考】
转载:原文链接:https://blog.csdn.net/x1131230123/article/details/134291339
二.vim编辑器
图示例:

2-1 Linux编辑器-vim使用
vi/vim的区别简单点来说,它们都是多模式编辑器,不同的是vim是vi的升级版本,它不仅兼容vi的所有指令,⽽且还有⼀些新的特性在⾥⾯。例如语法加亮,可视化操作不仅可以在终端运⾏,也可以运⾏于x window、 mac os、 windows。

2-2 vim的基本概念
• 正常/普通/命令模式(Normal mode)
控制屏幕光标的移动,字符、字或⾏的删除,移动复制某区段及进⼊Insert mode下,或者到 lastline mode
• 插⼊模式(Insert mode)
只有在Insert mode下,才可以做⽂字输⼊,按ESC键可回到命令⾏模式。
• 末⾏模式(last line mode)
⽂件保存或退出,也可以进⾏⽂件替换,找字符串,列出⾏号等操作。
在命令模式下,*shift+:* 即可进⼊该模式。要查看你的所有模式:打开 vim,底⾏模式直接输⼊:help vim-modes

2-3基本操作
进⼊vim,在系统提⽰符号输⼊vim及⽂件名称后,就进⼊vim全屏幕编辑画⾯: vim test.c不过有⼀点要特别注意,就是你进⼊vim之后,是处于[正常模式],你要切换到[插⼊模式]才能够 [正常模式]切换⾄[插⼊模式]输⼊⽂字。
• 输⼊a进入插入模式,从当前光标所在的位置的下一个位置开始输入文字
• 输⼊i进入插入模式,从当前光标所在的位置开始输入文字
• 输⼊o进入插入模式,插入新的一行,从行首开始输入文字
[插⼊模式]切换⾄[正常模式]
• ⽬前处于[插⼊模式],就只能⼀直输⼊⽂字,如果发现输错了字,想⽤光标键往回移动,将该字删除,可以先按⼀下「ESC」键转到[正常模式]再删除⽂字。当然,也可以直接删除。
• [shift + ;], 其实就是输⼊[:]退出vim及保存⽂件,在[正常模式]下,按⼀下「:」冒号键进⼊「Last line mode」,例如:
• : w (保存当前⽂件)
• : wq (输⼊「wq」,存盘并退出vim)
• : q! (输⼊q!,不存盘强制退出vim)
2-4 vim正常模式命令集
正常模式==命令模式
• 插⼊模式
- 按「i」切换进⼊插⼊模式「insert mode」,按“i”进⼊插⼊模式后是从光标当前位置开始输⼊
⽂件;
- 按「a」进⼊插⼊模式后,是从⽬前光标所在位置的下⼀个位置开始输⼊⽂字;
- 按「o」进⼊插⼊模式后,是插⼊新的⼀⾏,从⾏⾸开始输⼊⽂字。
• 从插⼊模式切换为命令模式
- 按「ESC」键。
(1)移动光标
- vim可以直接⽤键盘上的光标来上下左右移动,但正规的vim是⽤⼩写英⽂字⺟h、j、k、l,分别控制光标左、下、上、右移⼀格
- 按G:移动到⽂章的最后
- 按「 $ 」:移动到光标所在⾏的“⾏尾”
- 按「^」:移动到光标所在⾏的“⾏⾸”
- 按「w」:光标跳到下个字的开头
- 按「e」:光标跳到下个字的字尾
- 按「b」:光标回到上个字的开头
- 按「#l」:光标移到该⾏的第#个位置,如:5l,56l
- 按[gg]:进⼊到⽂本开始
- 按[shift+g]:进⼊⽂本末端
- 按「ctrl」+「b」:屏幕往“后”移动⼀⻚
- 按「ctrl」+「f」:屏幕往“前”移动⼀⻚
- 按「ctrl」+「u」:屏幕往“后”移动半⻚
- 按「ctrl」+「d」:屏幕往“前”移动半⻚
(2) 删除⽂字
- 「x」:每按⼀次,删除光标所在位置的⼀个字符
- 「#x」:例如,「6x」表⽰删除光标所在位置的“后⾯(包含⾃⼰在内)”6个字符
- 「X」:⼤写的X,每按⼀次,删除光标所在位置的“前⾯”⼀个字符
- 「#X」:例如,「20X」表⽰删除光标所在位置的“前⾯”20个字符
- 「dd」:删除光标所在⾏
「#dd」:从光标所在⾏开始删除#⾏
(3)复制文字
- 「yw」:将光标所在之处到字尾的字符复制到缓冲区中。
- 「#yw」:复制#个字到缓冲区
- 「yy」:复制光标所在⾏到缓冲区。
- 「#yy」:例如,「6yy」表⽰拷⻉从光标所在的该⾏“往下数”6⾏⽂字。
- 「p」:将缓冲区内的字符贴到光标所在位置。注意:所有与“y”有关的复制命令都必须
与“p”配合才能完成复制与粘贴功能。
•(4)替换操作
- 「r」:替换光标所在处的字符。
- 「R」:替换光标所到之处的字符,直到按下「ESC」键为⽌。
(5)撤销上⼀次操作(常用)
- 「u」:如果您误执⾏⼀个命令,可以⻢上按下「u」,回到上⼀个操作。按多次“u”可以执行多次回复
- 「ctrl + r」: 撤销的恢复(一旦退出无法进行撤销,但你只是保存没有进行q退出任然可以进行编辑)
(6)更改
- 「cw」:更改光标所在处的字到字尾处
- 「c#w」:例如,「c3w」表⽰更改3个字
(7)跳⾄指定的⾏
- 「ctrl」+「g」列出光标所在⾏的⾏号。
- 「#G」:例如,「15G」,表⽰移动光标⾄⽂章的第15⾏⾏⾸。
(8)字母大小写转换
shift+~:先按下 shift 键,再按下波浪号 ~ 不要停,往后遇到的所有小写字母将被转成大写,所有大写字母将被转成小写。
(9)查找单词
- shift+3=#选中单词
- n:进行逆向查找
2-5 vim底行模式命令集
注意:在使⽤底⾏模式之前,请记住先按「ESC」键确定您已经处于正常模式,再按「:」冒号即可进⼊底⾏模式。
(1) 列出⾏号
- 「set nu」: 输⼊「set nu」后,会在⽂件中的每⼀⾏前⾯列出⾏号。
(2) 跳到⽂件中的某⼀⾏
- 「#」:「#」号表⽰⼀个数字,在冒号后输⼊⼀个数字,再按回⻋键就会跳到该⾏了,如输⼊数字15,再回⻋,就会跳到⽂章的第15⾏。
(3) 查找字符
- 「/关键字」: 先按「/」键,再输⼊您想寻找的字符,如果第⼀次找的关键字不是您想要的,可以⼀直按「n」会往后寻找到您要的关键字为⽌。(常用)
- 「?关键字」:先按「?」键,再输⼊您想寻找的字符,如果第⼀次找的关键字不是您想要的,可以⼀直按「n」会往前寻找到您要的关键字为⽌。
(4) 保存⽂件
- 「w」: 在冒号输⼊字⺟「w」就可以将⽂件保存起来,如果要强制保存可以再w后面加一个!强制保存w!
(5) 离开vim
- 「q」:按「q」就是退出,如果⽆法离开vim,可以在「q」后跟⼀个「!」强制离开vim。
- 「wq」:⼀般建议离开时,搭配「w」⼀起使⽤,这样在退出的时候还可以保存⽂件。(常用)
(6)查看函数手册
- !man[选项][函数名](按q键退出手册).(常用)
(7)批量化替换字符
%s/printf/cout/g(把文中所有 printf 替换成 cout,g --global 表示全局的意思)(常用)(8)多文件多屏操作
如果想把 test.c 文件中的 10 行代码复制 test1.c 文件中,该如何操作呢?
vs test1.c(在 vim 中打开 test1.c 文件,左右分屏)
再按 ctrl + ww 组合键可以切换文件(w 要按两下)。
(9)跑任何想跑的命令
- 格式:
!命令(! 表示底行执行 bash 命令),举例:
vim小技巧:
1.src+n就可以回到你要指定的行数,重新打开源文件的话
2.!+字母就表示最近开始的这个以它为开头的命令行

2-7 简单vim配置
(1)配置⽂件的位置
• 在⽬录 /etc/ 下⾯,有个名为vimrc的⽂件,这是系统中公共的vim配置⽂件,对所有⽤⼾都有效。
• ⽽在每个⽤⼾的主⽬录下,都可以⾃⼰建⽴私有的配置⽂件,命名为:“.vimrc”。如,/root⽬录下,通常已经存在⼀个.vimrc⽂件,如果不存在,则创建之。
• 切换⽤⼾成为⾃⼰执⾏ su ,进⼊⾃⼰的主⼯作⽬录,执⾏ cd ~
• 打开⾃⼰⽬录下的.vimrc⽂件,执⾏ vim .vimr
(2)常⽤配置选项,⽤来测试
• 设置语法⾼亮: syntax on
• 显⽰⾏号: set nu 如果要取消的话再set前面加"就可以了,进行注释
• 设置缩进的空格数为4: set shiftwidt
(3)使用插件
要配置好看的 vim ,原生的配置可能功能不全,可以选择安装插件来完善配置,保证用户是你要配置的用户。
【参考文档】
手把手教你把Vim改装成一个IDE编程环境(图文)_vim
https://github.com/wsdjeg/vim-galore-zh_cn
2-8 vim 中批量添加和删除注释
方法一:块选择模式(1)批量添加注释:进入 vim 编辑器,按 ctrl+v 进入块选择(视图)模式(visual block),然后移动光标选择要添加注释的行;
- 再按 shift+i / I 键(大写字母),进入 Insert 插入模式,输入你要插入的注释符(比如 //)。
- 最后按 Esc 键,你所选择的行就被注释上了
(2)批量删除注释:
- 同样按 ctrl+v 进入块选择(视图)模式,选中要删除的行首的注释符号,注意 // 要选中两个。选好之后按 d 键即可删除注释,Esc 保存退出。
//
方法二:替换命令
在底行模式下,可以采用替换命令进行注释:
(1)添加注释:起始行号, 结束行号 s/^/注释符/g(表示在 xx 到 xx 行加入注释符,^ 表示行首的意思),然后按下回车键,注释成功。
(2)删除注释:起始行号, 结束行号 s/^注释符//g(表示取消 xx 到 xx 行行首的注释符),然后按)下回车键,取消注释成功。
举例说明:

三.gcc/g++编辑器
3-1gcc/g++背景
程序运行过程:
预处理(进行宏替换)-->编译(生成汇编)-->汇编(生成机器可识别的代码)-->链接(生成可执行文件/库文件)
3-2 gcc编译选项
格式 gcc [选项] 要编译的⽂件 [选项] [⽬标⽂件]
【补充选项】
- -E 只激活预处理,这个不⽣成⽂件,你需要把它重定向到⼀个输出⽂件⾥⾯
- -S 编译到汇编语⾔不进⾏汇编和链接
- -c 编译到⽬标代码
- -o ⽂件输出到 ⽂件
- -static 此选项对⽣成的⽂件采⽤静态链接
- -g ⽣成调试信息。GNU 调试器可利⽤该信息。
- -shared 此选项将尽量使⽤动态库,所以⽣成⽂件⽐较⼩,但是需要系统由动态库.
- -O0, -O1, -O2, -O3 编译器的优化选项的4个级别,-O0表⽰没有优化,-O1为缺省值,-O3优化级别最⾼
- -w 不⽣成任何警告信息。
- -Wall ⽣成所有警告信息
备注:记忆小技巧 esc,iso
3-2-1 预处理(进⾏宏替换)
• 预处理功能主要包括宏定义,⽂件包含,条件编译,去注释等。
• 预处理指令是以#号开头的代码⾏。
• 实例: gcc –E hello.c –o hello.i
• 选项“-E”,该选项的作⽤是让 gcc 在预处理结束后停⽌编译过程。
• 选项“-o”是指⽬标⽂件,“.i”⽂件为已经过预处理的C原始程序。
3-2-2 编译(⽣成汇编)
• 在这个阶段中,gcc ⾸先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的⼯作,
在检查⽆误后,gcc 把代码翻译成汇编语⾔。
• ⽤⼾可以使⽤“-S”选项来进⾏查看,该选项只进⾏编译⽽不进⾏汇编,⽣成汇编代码。
• 实例: gcc –S hello.i –o hello.s
3-2-3 汇编(⽣成机器可识别代码)
• 汇编阶段是把编译阶段⽣成的“.s”⽂件转成⽬标⽂件
• 读者在此可使⽤选项“-c”就可看到汇编代码已转化为“.o”的⼆进制⽬标代码了
• 实例: gcc –c hello.s –o hello.o
3-2-4 连接(⽣成可执⾏⽂件或库⽂件)
• 在成功编译之后,就进⼊了链接阶段。
• 实例: gcc hello.o –o hello
3-3 动态链接和静态链接
在我们的实际开发中,不可能将所有代码放在⼀个源⽂件中,所以会出现多个源⽂件,⽽且多个源⽂件之间不是独⽴的,⽽会存在多种依赖关系,如⼀个源⽂件可能要调⽤另⼀个源⽂件中定义的函数,但是每个源⽂件都是独⽴编译的,即每个*.c⽂件会形成⼀个*.o⽂件,为了满⾜前⾯说的依赖关系,则需要将这些源⽂件产⽣的⽬标⽂件进⾏链接,从⽽形成⼀个可以执⾏的程序。这个链接的过程就是静态链接
静态链接的缺点很明显:
• 浪费空间:因为每个可执⾏程序中对所有需要的⽬标⽂件都要有⼀份副本,所以如果多个程序对同⼀个⽬标⽂件都有依赖,如多个程序中都调⽤了printf()函数,则这多个程序中都含有printf.o,所以同⼀个⽬标⽂件都在内存存在多个副本;
• 更新⽐较困难:因为每当库函数的代码修改了,这个时候就需要重新进⾏编译链接形成可执⾏程序。但是静态链接的优点就是,在可执⾏程序中已经具备了所有执⾏程序所需要的任何东西,在执⾏的时候运⾏速度快。
动态链接:
动态链接的出现解决了静态链接中提到问题。动态链接的基本思想是把程序按照模块拆分成各个相对独⽴部分,在程序运⾏时才将它们链接在⼀起形成⼀个完整的程序,⽽不是像静态链接⼀样把所有程序模块都链接成⼀个单独的可执⾏⽂件。
动态链接其实远⽐静态链接要常⽤得多

3-4 动态库和静态库
• 我们的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 命令验证。
注意1:
• Linux下,动态库XXX.so, 静态库XXX.a
• Windows下,动态库XXX.dll, 静态库XXX.lib
⼀般我们的云服务器,C/C++的静态库并没有安装,可以采⽤如下⽅法安装
# Centos
yum install glibc-static libstdc++-static -y
#ubuntu
普通用户权限不可以,得提高权限用sudo或者切换超级用户
【问题补充】
1.Linux 下生成的可执行程序,默认是动态链接的,如何查看呢?
(1)使用 ldd [filename] 命令可查看可执行文件的库依赖关系。
(2)使用 file [filename] 命令可以查看可执行文件的信息和类型。
2.想要生成的可执行程序是静态链接的,该如何做呢?
输入指令:gcc test.c -o test_s -static
四.⾃动化构建-make/Makefile
4-1 背景
• ⼀个⼯程中的源⽂件不计数,其按类型、功能、模块分别放在若⼲个⽬录中,makefile定义了⼀系列的规则来指定,哪些⽂件需要先编译,哪些⽂件需要后编译,哪些⽂件需要重新编译,甚⾄于进⾏更复杂的功能操作
• makefile带来的好处就是⸺“⾃动化编译”,⼀旦写好,只需要⼀个make命令,整个⼯程完全⾃动编译,极⼤的提⾼了软件开发的效率。
• make是⼀个命令⼯具,是⼀个解释makefile中指令的命令⼯具,⼀般来说,⼤多数的IDE都有这。
• make是⼀条命令,makefile是⼀个⽂件,(文件中保存的是目标文件和原始文件间的依赖关系和依赖方法)两个搭配使⽤,完成项⽬⾃动化构建。
4-2基本使用
假设我们创建了一个源文件myproc.c,现在把他编译成可执行文件
方法一:
直接使用gcc命令
![]()
方法二:
使用make命令,创建一个makefile的文件通过make的命令进行编译
那如何创建makefiel呢?那就要了解依赖关系和依赖方法进行学习
依赖关系和依赖方法:
依赖关系:就是我依赖于谁
myproc.o 依赖myproc,myproc.o依赖myproc.s,myproc.s依赖于myproc.i,myproc.i依赖于myproc.c
依赖方法:对应的那个方法是如何生成我想要的结果
编写makefie文件:

编写好之后,会用make命令

.PHONY的作用



简化makefile

当会出现多个文件的makefile

这里的头文件可以不用放进来因为编译代码它可以自动展开,bian把头文件的代码拷贝到源文件中,所以找到头文件一般比较重要,通过系统路径,当前路径
注意:一般遇到的项目比较多时,可以不用gcc命令进行,可以用make/makefile进行自动化构建项目
.PHONY解释(关键字)
phony 本身不是修饰词,而是 Makefile 里的一个特殊目标(伪目标)声明,它的作用是告诉 make :“别把我当成一个叫 phony 的文件,也别去检查它的时间戳,只要我被作为目标执行,就无条件执行它下面的命令。”phony 本身不是修饰词;
而是 Makefile 里的一个特殊目标(伪目标)声明,它的作用是告诉 make :“别把我当成一个叫 phony 的文件,也别去检查它的时间戳,只要我被作为目标执行,就无条件执行它下面的命令。”
项⽬清理
⼯程是需要被清理的像clean这种,没有被第⼀个⽬标⽂件直接或间接关联,那么它后⾯所定义的命令将不会被⾃动执⾏,不过,我们可以显⽰要make执⾏。即命令⸺“make clean”,以此来清除所有的⽬标⽂件,以便重编译。
• 但是⼀般我们这种clean的⽬标⽂件,我们将它设置为伪⽬标,⽤ .PHONY 修饰,伪⽬标的特性是,总是被执⾏的。
什么叫做总是被执⾏?

总结:.PHONY:让make忽略源⽂件和可执⾏⽬标⽂件的M时间对⽐
4-3make的推导过程


编译:

从上到下进行推导过程,从下到上执行依赖方法

make时如何工作的?
make是如何⼯作的,在默认的⽅式下,也就是我们只输⼊make命令。那么:
- 1. make会在当前⽬录下找名字叫“Makefile”或“makefile”的⽂件。
- 2. 如果找到,它会找⽂件中的第⼀个⽬标⽂件(target),在上⾯的例⼦中,他会找到 myproc 这个⽂件,并把这个⽂件作为最终的⽬标⽂件。
- 3. 如果 myproc ⽂件不存在,或是 myproc 所依赖的后⾯的 myproc.o ⽂件的⽂件修改时间要⽐ myproc 这个⽂件新(可以⽤ touch 测试),那么,他就会执⾏后⾯所定义的命令来⽣成myproc 这个⽂件。
- 4. 如果 myproc 所依赖的 myproc.o ⽂件不存在,那么 make 会在当前⽂件中找⽬标为myproc.o ⽂件的依赖性,如果找到则再根据那⼀个规则⽣成 myproc.o ⽂件。(这有点像⼀个堆栈的过程)
- 5. 当然,你的C⽂件和H⽂件是存在的啦,于是 make 会⽣成 myproc.o ⽂件,然后再⽤myproc.o ⽂件声明 make 的终极任务,也就是执⾏⽂件 hello 了。
- 6. 这就是整个make的依赖性,make会⼀层⼜⼀层地去找⽂件的依赖关系,直到最终编译出第⼀个⽬标⽂件。
- 7. 在找寻的过程中,如果出现错误,⽐如最后被依赖的⽂件找不到,那么make就会直接退出,并报错,⽽对于所定义的命令的错误,或是编译不成功,make根本不理。
- 8. make只管⽂件的依赖性,即,如果在我找了依赖关系之后,冒号后⾯的⽂件还是不在,那么对不起,我就不⼯作啦。
【补充】
五. Linux第⼀个系统程序−进度条
5-1.回⻋,换⾏概念
- 回车:\r进行表示。即回到当前行的最开始的位置,此时如果你进行数据的添加会不断覆盖你之前写过的数据.
- 换行。
- 回车换行:就是光标移到下一行最开始位置
补充;c语言当中就是\n表示回车换行;键盘上就是enter进行回车换行
5-2缓冲区的概念
把下面的两个代码放进linux操作系统会发生啥?
#include <stdio.h>
#include <unistd.h>//sleep()
int main()
{
printf("hello bite!");//这里面没有\n
sleep(3);
return 0;
}
观察它的现象:先休眠3秒结束接着打印出hello bite 结束程序
#include <stdio.h>
#include <unistd.h>//sleep()
int main()
{
printf("hello bite!\n");//有\n
sleep(3);
return 0;
}
观察它的现象:先打印出hello bite然后休眠3,结束程序
当 sleep(3); 执行的时候,printf("hello bite\n"); 已经执行完了,但却没有先打印字符串,这是为什么呢?
printf("hello world"); 已经执行完了,但并不代表字符串就得显示出来。
那在执行 sleep(3); 期间,字符串在哪里呢?
\n缓冲区(就是一段内存空间,可以暂存临时数据,在合适的时候刷新出去)。
【补充】
刷新:内存条为了不忘记数据,必须每隔很短的时间(比如64毫秒)就把所有存储单元里的电信号重新读一遍、再写一遍。一般就是把数据真正写入磁盘,文件显示器,网络等设备或者文件当中。
任何的c语言程序启动的时候需要的三个流

在没有\n刷新的前提下可以通过这个函数进行强制刷新
#include <stdio.h>
#inlcude <unistd.h>
int main()
{
printf("hello bite!");
fflush(stdout);//刷新函数 强制刷新
sleep(3);
return 0;
}
【解释现象】
我们想要把字符串显示到显示器上,显示器默认是行刷新,遇到 '\n' 才刷新,但是我们前面写的代码中,并没有 '\n',所以 printf 执行完并没有刷新。'\n'为了在 printf 执行完的时候,让字符串立马显示出来,需要进行强制刷新,把字符串尽快的写入显示器中。同时没有\n的时候可以通过这个函数进行强制刷新
5-3展示进度条代码:
源文件
process.c
#include "process.h"//fflush
#include <string.h>//memset
#include <unistd.h>//usleep
#define NUM 101//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(版本2)
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);
测试文件
$ cat 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();
DownLoad();
DownLoad();
DownLoad();
DownLoad();
DownLoad();
DownLoad();
DownLoad();
return 0;
}
自动化编译展示:

结果显示:

5-4倒计时小程序
#include <stdio.h>
#include <unistd.h>
int main()
{
int i = 10;
while(i >= 0)
{
printf("%-2d\r", i); // \n
fflush(stdout);
i--;
sleep(1);
}
printf("\n");
return 0;
}
现象:倒计时计数从10到0进行覆盖计数
【补充】
在C语言中,
\r代表回车(Carriage Return) 控制字符,ASCII码是13( 只回车,回到行首覆盖写)
【扩展】
内存里的二进制,到了屏幕上为什么变成了字符?
你有没有想过这样一个问题:int count = 123456;在内存里只占 4 个字节,但用 printf 打印到屏幕上,却显示成了 6 个字符?
- 原因很简单:内存是给 CPU 看的,显示器是给人看的。CPU 认识二进制(0101...),但人只认识字符('1'、'2'、'3'...)。
- 所以 printf的本质工作,就是一次“翻译”——把内存里的 4 字节二进制整数,转换成 6 个独立的字符,再一个个发送到显示器上。反过来,`scanf` 做的是逆向翻译:把你敲下的 '1' '2' '3' '4' '5' '6' 这 6 个字符,重新拼回一个 4 字节的整数存进内存。
这就解释了为什么键盘和显示器被统称为“字符设备”——它们只认字符,不认二进制。也解释了为什么文本文件和二进制文件不一样:文本文件存的是转换后的字符(给人看的),二进制文件存的是内存原样(给机器读的)。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐









所有评论(0)