Linux:环境基础开发工具使用

文章目录

  • 前言
  • 1.Linux下的软件安装
    • 1.1 什么是软件包
    • 1.2 如何安装软件
    • 1.3 如何卸载软件
  • 2.vim
    • 2.1 vim的基本概念
    • 2.2 vim的基本操作
    • 2.3 vim正常模式命令集
    • 2.4 vim末行模式命令集
    • 2.5 vim的操作总结
  • 3.Linux下的编译器:gcc
    • 3.1 gcc的使用
    • 3.2 gcc是如何工作的
      • 3.2.1 预处理(进行宏替换)
      • 3.2.2 编译(生成汇编)
      • 3.2.3 汇编(生成机器可识别代码)
      • 3.2.4 链接(生成可执行文件或库文件)
    • 3.3 gcc选项
    • 3.4 函数库
  • 4.Linux下的调试器:gdb
    • 4.1 gdb的使用
  • 5.Linux项目自动化构建工具:make/Makefile
    • 5.1 原理
    • 5.2 项目清理
      • 文件的时间属性
    • 5.3 Makefile的一些语法
  • 6.模拟实现一个进度条
    • 6.1 缓冲区
    • 6.2 回车换行
    • 6.3 模拟实现
      • 6.3.1 process.h
      • 6.3.2 process.c
      • 6.3.3 main.c
  • 总结

前言

  前面已经介绍了一些指令内容,本章将会继续介绍在Linux上安装软件,已经编译器的使用,代码的调试等等内容。

1.Linux下的软件安装

1.1 什么是软件包

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

1.2 如何安装软件

  安装软件一般都是root用户来进行,这里是用sudo来进行了提权。一般想要安装什么软件直接上网搜索就可以了。
在这里插入图片描述

注意事项:

  • 安装软件时由于需要向系统目录中写入内容,一般需要 sudo 或者切到 root 账户下才能完成。
  • yum安装软件只能一个装完了再装另一个,正在yum安装一个软件的过程中,如果再尝试用yum安装另外一个软件,yum会报错。
  • 出现 “complete” 字样, 说明安装完成。

1.3 如何卸载软件

  仍然是一条命令:

  • sudo yum remove 软件名

2.vim

2.1 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

2.2 vim的基本操作

  • 进入vim,在系统提示符号输入vim及文件名称后,就进入vim全屏幕编辑画面:

vim code.c
不过有一点要特别注意,就是你进入vim之后,是处于[正常模式],你要切换到[插入模式]才能够输入文字。

  • [正常模式]切换至[插入模式]

输入a
输入i
输入o

  • [插入模式]切换至[正常模式]

目前处于[插入模式],就只能一直输入文字,如果发现输错了字,想用光标键往回移动,将该字删除,可以先按一下「ESC」键转到[正常模式]再删除文字。当然,也可以直接删除。

  • [正常模式]切换至[末行模式]

「shift + : 」, 其实就是输入「:」

  • 退出vim及保存文件,在[正常模式]下,按一下「:」冒号键进入「Last line mode,例如:
: w (保存当前文件)
wq (输入「wq」,存盘并退出vim)
q! (输入q!,不存盘强制退出vim)

2.3 vim正常模式命令集

  • 插入模式

按「i」切换进入插入模式「insert mode」,按“i”进入插入模式后是从光标当前位置开始输入文件;
按「a」进入插入模式后,是从目前光标所在位置的下一个位置开始输入文字;
按「o」进入插入模式后,是插入新的一行,从行首开始输入文字。

  • 从插入模式切换为命令模式

按「ESC」键。

  • 移动光标

vim可以直接用键盘上的光标来上下左右移动,但正规的vim是用小写英文字母「h」、「j」、「k」、「l」,分别控制光标左、下、上、右移一格。
按「G」:移动到文章的最后
按「 $ 」:移动到光标所在行的“行尾”
按「^」:移动到光标所在行的“行首”
按「w」:光标跳到下个字的开头
按「e」:光标跳到下个字的字尾
按「b」:光标回到上个字的开头
按「#l」:光标移到该行的第#个位置,如:5l,56l
按[gg]:进入到文本开始
按[shift+g]:进入文本末端
按「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行行首。

2.4 vim末行模式命令集

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

  • 列出行号

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

  • 跳到文件中的某一行

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

  • 查找字符

  「/关键字」: 先按「/」键,再输入您想寻找的字符,如果第一次找的关键字不是您想要的,可以一直按 「n」会往后寻找到您要的关键字为止。
  「?关键字」:先按「?」键,再输入您想寻找的字符,如果第一次找的关键字不是您想要的,可以一直 按「n」会往前寻找到您要的关键字为止。
  问题:/ 和 ?查找有和区别?操作实验一下

  • 保存文件

「w」: 在冒号输入字母「w」就可以将文件保存起来

  • 离开vim

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

2.5 vim的操作总结

  • 三种模式

正常模式
插入模式
底行模式

  • vim操作

打开,关闭,查看,查询,插入,删除,替换,撤销,复制等等操作。

  有想了解更多关于vim的使用可以参考下面的链接:

vim从入门到nb

  补充如何注释:

  ctrl+v进入视图模式,然后通过hjkl来选择行,再按shift+i,再按//,再按esc,就可以将选中行注释掉。

  补充如何打开多个文件:

  在底行模式下输入:vs 文件名 ,就可以了,切换文件用ctrl+ww即可。
在这里插入图片描述

  补充在打开文件时,指定光标所在位置:

gcc test.c +7 ,意思就是打开文件时,光标处于第七行,方便在编译出错时快速找到出错的行。

3.Linux下的编译器:gcc

3.1 gcc的使用

在这里插入图片描述
  在gcc后面跟上文件名就可以了。

3.2 gcc是如何工作的

  其实具体过程与windows下生成可执行程序的过程是一样的,都是经过 预处理(进行宏替换),编译(生成汇编),汇编(生成机器可识别代码),连接(生成可执行文件或库文件)。

3.2.1 预处理(进行宏替换)

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

在这里插入图片描述
在这里插入图片描述

  在hello.i文件中将上面说的预处理功能进行了一遍,但仍然是C语言,上面展开的就是<stdio.h>头文件。

3.2.2 编译(生成汇编)

  • 在这个阶段中,gcc 首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,gcc 把代码翻译成汇编语言。
  • 用户可以使用“-S”选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。
  • 实例: gcc –S hello.i –o hello.s
    在这里插入图片描述

在这里插入图片描述
  此步完成之后里面就不再是c语言了,而是被转化为了汇编。

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

  • 汇编阶段是把编译阶段生成的“.s”文件转成目标文件
  • 读者在此可使用选项“-c”就可看到汇编代码已转化为“.o”的二进制目标代码了
  • 实例: gcc –c hello.s –o hello.o
    在这里插入图片描述
    在这里插入图片描述
      此步完成后就转化为了最基本的二进制语言,但是我们看到只是一堆乱码。
    在这里插入图片描述
      但是我们可以通过相关的软件来看。

3.2.4 链接(生成可执行文件或库文件)

  • 在成功编译之后,就进入了链接阶段。
  • 实例: gcc hello.o –o hello
    在这里插入图片描述
      链接完之后就生成了可执行程序。(.o文件+系统库 = 可执行程序)

  在平时我们用gcc编译时,gcc编译器是将所以过程一次性走完了,直接得到了可执行程序。而通过-E、-S、-c等选项可以让其分开完成。

3.3 gcc选项

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

3.4 函数库

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

  也就是说我们现在所写的代码,都是站在巨人的肩膀上,已经有人为我们写好了,我们只是调用了别人已经写的接口或者函数。(用以激励自我,难度站在巨人的肩膀上还学不明白吗?)
  那么我们所说的这批功能函数在哪里呢?没错,它就是库。在Linux中我们可以通过ldd指令来查看一个可执行文件依赖的那些库。

在这里插入图片描述

   库的命名一般是去掉前缀和后缀,以上面的例子来说就去掉lib,再去掉.so.6,实际上它就是C标准库
  由此我们可以看出库文件就存放在/lib64/目录下的,也就是说我们的可执行程序 = 代码 + 头文件(提供方法的声明) +库(提供方法的实现)。
   所以所谓的开发环境安装实际上就是下载拷贝头文件和库到指定目录下(这个目录可以被编译器自己找到)

  函数库一般分为静态库和动态库两种。

  • 静态库是指编译链接时,把库文件的代码全部拷贝到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为“.a”
  • 动态库与之相反,在编译链接时并没有把库文件的代码拷贝到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为“.so”,如前面所述的 libc.so.6 就是动态库。gcc 在编译时默认使用动态库。完成了链接之后,gcc 就可以生成可执行文件,如下所示。 gcc hello.o –o hello
  • 库中所有函数都有地址,所谓的动态链接就是把要链接的库中的函数地址拷贝到我们的可执行程序的特定位置。
  • gcc默认生成的二进制程序,是动态链接的,这点可以通过 file 命令验证。

  动态库的优缺点:

  • 优点:形成的可执行程序的体积比较小,比较节省资源。

  • 缺点:稍慢一些

  静态库的优缺点:

  • 优点:无视库,可以独立运行
  • 缺点:体积太大,浪费资源

  gcc的默认链接的方式是动态链接,为什么呢?就是因为静态库的缺点无法接受,所以选择了动态链接。

  我们下载的开发环境,默认要为我们做什么?

  1. 下载开发环境include,lib
  2. 设置合理的查找路径
  3. 规定我们形成的可执行程序的链接方式

  为什么要有库呢?是为了大家不必实现一些不必要的功能,使用现成的就好,不然你实现一遍,他又实现一遍,那到底是使用谁的呢?所以就直接让大家使用同一份写好的就好了。

4.Linux下的调试器:gdb

  • 程序的发布方式有两种,debug模式和release模式
  • Linux gcc/g++出来的二进制程序,默认是release模式
  • 要使用gdb调试,必须在源代码生成二进制程序的时候, 加上 -g 选项

在这里插入图片描述
  这个意思就是此时没有发现debug的特征,所以可以知道编译完成之后的可执行程序是release版本的。
在这里插入图片描述
在这里插入图片描述
  重新编译之后就可以进入调试了。
  如果想让你的代码以debug的形式发布,必须给gcc添加-g选项

4.1 gdb的使用

  • gdb binFile 退出: ctrl + d 或 quit

调试命令:

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

  eg: 增加断点
在这里插入图片描述
在这里插入图片描述

5.Linux项目自动化构建工具:make/Makefile

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

5.1 原理

  • make是如何工作的,在默认的方式下,也就是我们只输入make命令。
  1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
  2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“hello”这个文件, 并把这个文件作为最终的目标文件。
  3. 如果hello文件不存在,或是hello所依赖的后面的hello.o文件的文件修改时间要比hello这个文件新(可以用 touch 测试),那么,他就会执行后面所定义的命令来生成hello这个文件。
  4. 如果hello所依赖的hello.o文件不存在,那么make会在当前文件中找目标为hello.o文件的依赖性,如果找到则再根据那一个规则生成hello.o文件。(这有点像一个堆栈的过程)
  5. 当然,你的C文件和H文件是存在的,于是make会生成 hello.o 文件,然后再用 hello.o 文件声明 make的终极任务,也就是执行文件hello了。
  6. 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
  7. 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错, 而对于所定义的命令的错误,或是编译不成功,make根本不理。
  8. make只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起, 我就要罢工了。

在这里插入图片描述
  这里就是你要生成mybin可执行程序,需要依赖hello.c来生成,通过什么样的方式来生成呢?通过gcc hello.c -o mybin来生成。

5.2 项目清理

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

在这里插入图片描述
在这里插入图片描述
  这样看好像还不如直接编译,对于只编译一个文件确实是这样的,如果是编译十个文件或者更多,它的优势就体现出来, 我们只需要make就可以直接编译了,不然就需要将所有文件都得写一遍,没编译一次就得将所有文件都斜写一遍,很麻烦。
  .PHONY是伪目标的意思,被伪目标修饰的文件总是被执行的。我们来看:此时是无法进行多次编译的
在这里插入图片描述
  .当经过.PHONY修饰后就可以进行多次编译了。
在这里插入图片描述
在这里插入图片描述
  这是为什么呢?这是因为make和makefile是不允许进行多以编译的,因为它会识别你的文件是否进行过更改,如果没有被更改它是不会进行编译的,只会编译修改过的文件,因为没有修改过的文件进行反复编译是没有意义的,同时也可以提高编译速度。
  那么它又是如何辨别出来一个文件是否被修改过呢?这是因为每个文件的属性中都有记录本文件的最后修改时间,即使如此哪有是如何对比出来的呢?它实际上是与这个源文件所生成的可执行程序的生成时间进行比较的。我们知道一定是现有源文件,再有可执行程序,那么源文件的时间一定是小于可执行程序文件的时间的,而一旦源文件进行了修改,那么它的时间就会大于可执行程序文件的时间,make和makefile就是由此进行对比然后绝对一个文件是否被再次编译。

文件的时间属性

  那我们就来看看文件的几个时间属性:
在这里插入图片描述

  这里就有一个问题了,Modify与Change都是修改,那么它们有什么区别呢?我们知道一个文件 = 内容 + 属性,Modify就是针对内容的修改,Change是针对属性的修改。
  那么make和makefile是通过比较哪一个时间来得出的结论呢?我们可以看出,如果一个文件内容修改了,那么文件的大小就会变化,那么跟着它的Change就会因为文件大小这个属性发生了改变而跟着改变。而当Change改变时,只是属性发生的变化,而内容可能没有发生变化(比如文件的权限发生了变化)。由此可以得出make和makefile是根据Modify的时间来进行比较的

  但是我们发现我们在查看一个文件后,它的访问时间并没有发生改变。这又是为什么呢?

  我们知道文件都是存放在磁盘中的,而更改访问时间的本质就是访问磁盘,但是我们知道访问磁盘是比较满的,而我们又需要经常查看文件,这变相就减慢了系统的效率,因此在现在使用的Linux系统中,将Access时间的更改增加了一些策略,比如达到多少次才更新,也可能过了一定的时间才更新。

在这里插入图片描述
  那么我们有没有在不改变文件内容的前提下,更改文件时间呢?那就是我们之气使用创建文件的指令——touch。我们可以通过man touch来查看touch指令的使用方式。
在这里插入图片描述

5.3 Makefile的一些语法

  make和makefile是具有推导功能的。我们先修改一下Makefile文件。
在这里插入图片描述
  我们将hello.c改为hello.o。

在这里插入图片描述
  也就是说我们是要根据hello.o文件生成mybin可执行程序,但是我们没有hello.o文件,只有.c文件。此时Makefile就会自动通过.c文件进行推到出来.o文件,再生成mybin可执行程序。
  在Makefile文件中加上@后,就不会显现出指令。
在这里插入图片描述
在这里插入图片描述
  在Makefile中也支持书写变量。
在这里插入图片描述
  可以理解为是一种替换。这样写的好处就是我们每次只需要改上面的变量,下面的会自动跟着变,不再需要全部重新写了。
  Makefile的依赖方式还可以进行简写。
在这里插入图片描述
  这样写的好处是当多个文件一起进行编译时,下面只需要些一个$^就可以了。

6.模拟实现一个进度条

  在具体实现进度条之前还需要先了解一下缓冲区的概念。

6.1 缓冲区

在这里插入图片描述
在这里插入图片描述
  上面代码的区别仅仅是是否有\n,可以结果输出却不一样,这是为什么呢?我们知道程序是从上往下进行的,那么printf一定比sleep先执行,可是在下面的结果中却是先睡眠了两秒才输出了hello Linux,那么在这期间,hello Linux跑哪去了呢?

  先说后面的问题,在这期间hello Linux是存放到了stdout的(标准输出流)缓冲区中。如何验证呢?我们可以通过fflush来验证,它的作用是立即刷新缓冲区。在这里插入图片描述
  所以所有的输出都是先存放在输出缓冲区中,在刷新缓冲区后才会显示出来。
\n是一种刷新的策略–行刷新。

6.2 回车换行

  回车换行我们都知道它是跑到下一行的开头,有两个步骤:一个是先换到下一行,另一个是跑到开头。在我们编代码时,\n就是回车换行的行为,而\r就仅仅只是回车的作用,也就是将光标换到开头。
  也就是说使用\r时,比如先输出9,再回车输出8,那么后面的8就会将前面输出的9覆盖掉。
在这里插入图片描述
  这样就可以完成一个倒计时的效果。(大家可以自己试验一下,博主就懒的录制视频了)。
  但是我们要注意的是,输出到我们屏幕上的数据实际上字符,而如果我们将数据从10开始输出,按照上面所讲,第一次输出的是10,第二是9要覆盖开头第一个字符,那么就变成了90,这是不是就出现问题了?所以我们可以修改输出格式为两个字符为一组:
在这里插入图片描述

在这里插入图片描述
  倒计时数字一般都是靠边输出的,如果解决呢?这个也很简单,只需要在这这里加上-就可以了。
在这里插入图片描述

6.3 模拟实现

6.3.1 process.h

#pragma once 
#include<stdio.h>
#include<unistd.h>
#include<string.h>
void process();
void process_v2();
void process_v3();                                                                                                                                      

6.3.2 process.c

  1#include"process.h"2 3 const char* str = "/-|\\";4 5 6 //void process()7 //{8 //    int rate = 0;9 //    char bar[101] = {0};10 //    int len = strlen(str);11 //12 //    while(rate <= 100)13 //    {14 //        printf("[%-100s][%d%%][%c]\r",bar,rate,str[rate%len]);15 //        fflush(stdout);16 //        usleep(1000*20);17 //        bar[rate++] = '#';18 //    }19 //    printf("\n");20 //}21 22 23 void process_v2(int rate)                                                                                                                               24 {25     static char bar[101] = {0};26     int len = strlen(str);27 28     if(rate >= 0 && rate <= 100)29		{30         printf("[%-100s][%d%%][%c]\r",bar,rate,str[rate%len]);31         fflush(stdout);32         usleep(1000*20);33         bar[rate] = '#';34         if(rate == 100) bar[rate] = '\0';35     }36 }                                                                                                                                                       37 38 void process_v3(double rate)39 {40     static char bar1[101] = {0};41     int len = strlen(str);42 43     static int cnt = 0;44     cnt++;45     cnt = cnt > len ? 0 : cnt;46 47     if(rate >= 0 && rate <= 100)48     {49         printf("[%-100s][%.1f%%][%c]\r",bar1,rate,str[cnt]);50         fflush(stdout);51 52         if((int)rate < 99)53         {54             bar1[(int)rate++] = '=';55             bar1[(int)rate] = '>';56		}57         else 58         {59            // bar[(int)rate] = '=';60            bar1[99] = '=';61         }62     }63 }

6.3.3 main.c

	1 #include"process.h"2 #define TARGET 1024*10243 #define DNUM 1024*104 5 typedef void (*callback_t)(int);6 7 void Download(callback_t cb)8 {9     int target = TARGET;10     int total = 0;11     12     while(total < target)13     {14         usleep(1000*40);15         total += DNUM;16        double rate = total*100 / target; 17         cb(rate);18     }19     printf("\n");20 }21 22 void Down()23 {                                                                                                                                                     24     int target = TARGET;25     int total = 0;26     27     while(total < target)28     {29		  total += DNUM;30         process_v2(total*100 / target);31         usleep(1000*20);32     }33 34     printf("\n");35 }36 37 int main()38 {39     Down();40     Download(process_v3);41     return 0;42 }

总结

  本文主要是讲解了如果在Linux命令行模式下如果进行编写代码,相信经过本文的介绍后大家已经能对Linux下的编程有了一定的了解,让命令行模式在大家面前不再陌生,那么关于如果在Linux下编写代码的内容就到此结束,往后就会讲解有关Linux操作系统的一些知识。希望能与大家一同进步噢。
  如果大家发现有什么错误的地方,可以私信或者评论区指出喔。我会继续深入学习Linux,希望能与大家共同进步,那么本期就到此结束,让我们下期再见!!觉得不错可以点个赞以示鼓励!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/617123.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

(我的创作纪念日)[MySQL]数据库原理7——喵喵期末不挂科

希望你开心&#xff0c;希望你健康&#xff0c;希望你幸福&#xff0c;希望你点赞&#xff01; 最后的最后&#xff0c;关注喵&#xff0c;关注喵&#xff0c;关注喵&#xff0c;大大会看到更多有趣的博客哦&#xff01;&#xff01;&#xff01; 喵喵喵&#xff0c;你对我真的…

C++的引用和内联函数,auto

什么是引用 引用就是取别名 可以给一个变量取多个别名,也可以给别名取别名 别名与本名拥有同一地址,对任意别名修改,也会同时修改其他别名和本名 引用的作用 引用的作用与指针重叠度很高 图中函数的参数int& a,int& b,a是x的别名,b是y的别名 则ab的交换就是xy的交…

【七 (2)FineBI FCP模拟试卷-平台新增用户留存分析】

目录 文章导航一、字段解释1、用户平台登录信息表格2、用户平台激活信息表格 二、需求三、操作步骤1、建立用户平台登录信息表格和用户平台激活信息表格的关联关系2、将用户平台激活信息表格的激活日期添加到用户平台登录信息表格3、新增公式列&#xff0c;计算激活时间和登录时…

JavaSE——常用API进阶二(3/8)-Date、SimpleDateFormat(构造器、常用的方法、用法示例、时间格式的常见符号)

目录 Date 构造器、常用的方法 用法示例 SimpleDateFormat 构造器、格式化时间的方法 时间格式的常见符号 用法示例 解析字符串时间成为日期对象 接下来会学习JDK8以前传统的日期、时间&#xff0c;以及JDK8开始新增的日期、时间&#xff1b;有部分项目还是有在使用JDK…

mac 配置前端开发环境brew,git,nvm,nrm

我的电脑是mac 3 pro 一、配置Homebrew 打开终端&#xff0c;执行指令 /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"查看版本 brew -v 安装nvm brew install nvm 再执行 brew reinstall nvm 我这边安装好了…

【Linux实践室】Linux高级用户管理实战指南:用户所属组变更操作详解

&#x1f308;个人主页&#xff1a;聆风吟_ &#x1f525;系列专栏&#xff1a;Linux实践室、网络奇遇记 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 一. ⛳️任务描述二. ⛳️相关知识2.1 &#x1f514;Linux查看用户所属组2.1.1 &#x1f47b;使…

Spring源码刨析之配置文件的解析和bean的创建以及生命周期

public void test1(){XmlBeanFactory xmlBeanFactory new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));user u xmlBeanFactory.getBean("user",org.xhpcd.user.class);// System.out.println(u.getStu());}先介绍一个类XmlBeanFac…

前 5 名 iPhone 数据恢复软件评测

如今&#xff0c;我们似乎将整个生活都放在手机和移动设备上。他们用许多照片、备忘录、日历日期等记录了我们的生活&#xff0c;我们总是假设这些信息在我们需要时随时可以访问。但是&#xff0c;有许多情况会导致iPhone上的数据丢失&#xff0c;例如iPhone被盗&#xff0c;损…

【JSON2WEB】 13 基于REST2SQL 和 Amis 的 SQL 查询分析器

【JSON2WEB】01 WEB管理信息系统架构设计 【JSON2WEB】02 JSON2WEB初步UI设计 【JSON2WEB】03 go的模板包html/template的使用 【JSON2WEB】04 amis低代码前端框架介绍 【JSON2WEB】05 前端开发三件套 HTML CSS JavaScript 速成 【JSON2WEB】06 JSON2WEB前端框架搭建 【J…

微信跳转页面时发生报错

报错如下图所示&#xff1a; 解决方法&#xff1a;&#xff08;从下面四种跳转方式中任选一种&#xff0c;哪种能实现效果就用哪个&#xff09; 带历史回退 wx.navigateTo() //不能跳转到tabbar页面 不带历史回退 wx.redirectTo() //跳转到另一个页面wx.switchTab() //只能…

【LAMMPS学习】八、基础知识(1.8)键的断裂

8. 基础知识 此部分描述了如何使用 LAMMPS 为用户和开发人员执行各种任务。术语表页面还列出了 MD 术语&#xff0c;以及相应 LAMMPS 手册页的链接。 LAMMPS 源代码分发的 examples 目录中包含的示例输入脚本以及示例脚本页面上突出显示的示例输入脚本还展示了如何设置和运行各…

【MySQL】:深入解析多表查询(下)

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; MySQL从入门到进阶 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一. 自连接1.1 自连接查询1.2 联合查询 二. 子查询2.1 概述2.2 分类2.3 标量子查…