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*1024
    3 #define DNUM 1024*10
    4 
    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,希望能与大家共同进步,那么本期就到此结束,让我们下期再见!!觉得不错可以点个赞以示鼓励!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Micromamba 安装与使用

文章目录 一、安装 MicromambaWindows安装使用 Micromamba 一、安装 Micromamba Micromamba 是一个静态链接的 C 可执行文件&#xff0c;在 Windows 上就是一个 micromamba.exe 文件&#xff0c;下载下来就直接可以用&#xff0c;甚至都不需要专门安装。唯一需要做的就是设置 …

Python编程的十大好用工具!

Python一直以来都是程序员们的首选编程语言之一&#xff0c;其灵活性和功能强大的库使其成为解决各种问题的理想选择。在本文中&#xff0c;我们将介绍Python编程的终极十大工具&#xff0c;这些工具可以帮助您在各种领域取得成功&#xff0c;无论您是初学者还是经验丰富的开发…

python数据分析学什么东西

前言 大家好&#xff0c;小编为大家解答python数据分析重点要学什么的问题。很多人还不知道python数据分析需要什么基础&#xff0c;现在让我们一起来看看吧&#xff01; 数据分析人人都有必要掌握一点&#xff0c;哪怕只是思维也行。下面探讨Python数据分析需要学习的知识范…

JavaScript-2.对话框、函数、数组、Date、DOM

对话框 window对象封装了三个对话框用于与用户交互 提示框&#xff1a;alert(title);确认框&#xff1a;confirm(title);输入框&#xff1a;prompt(title); 确认框 包含两个按钮“确认”/“取消”&#xff0c;点击确定时&#xff0c;返回值为true // 确认框 var bool con…

微服务项目——谷粒商城

文章目录 一、项目简介&#xff08;一&#xff09;完整的微服务架构详细解释——微服务划分图&#xff08;二&#xff09;电商模式1.B2B 模式2.B2C 模式3.C2B 模式4.C2C 模式5.o2o 模式2.谷粒商城 &#xff08;三&#xff09;项目技术&特色&#xff08;四&#xff09;项目前…

Transformer 结构浅析

Transformer 结构浅析 文章目录 Transformer 结构浅析Transformer 网络结构编码器位置编码多头注意力层Add&NormFeed Forward 解码器带掩码的多头注意力层多头注意力层 预测 Transformer 网络结构 Transformer模型的网络结构如图&#xff0c;且transformer结构主要分为两部…

Linux使用docker安装RocketMQ并登录管理界面

Linux使用docker安装RocketMQ并登录管理界面 1、创建 /opt/rocketmq/docker-compose.yml和/opt/rocketmq/broker.conf两个配置文件 2、docker-compose.yml&#xff0c;并配置管理页面端口为8090 version: 3.5 services:rmqnamesrv:image: foxiswho/rocketmq:servercontainer_…

“栈”顶到底是高地址还是低地址?

栈的增长方向永远是从杯底到杯顶&#xff0c;所以对于栈来说上面是栈底下面是栈顶&#xff0c;而对于堆来说&#xff0c;上面是堆顶下面是堆底。栈是连续分配内存的&#xff0c;如果给一个数组或对象分配内存&#xff0c;栈会选择还没分配的最小的内存地址给数组&#xff0c;在…

20240327-1-评测指标面试题

评测指标面试题 metric主要用来评测机器学习模型的好坏程度,不同的任务应该选择不同的评价指标,分类,回归和排序问题应该选择不同的评价函数. 不同的问题应该不同对待,即使都是分类问题也不应该唯评价函数论,不同问题不同分析. 回归(Regression) 平均绝对误差(MAE) 平均绝对…

Android 车载应用开发概述

前言 介绍 Android 车载应用开发 文章目录 前言一、Android Automotive OS 概述二、Android Automotive OS 架构三、常见的车载应用1、系统应用1&#xff09;SystemUI是什么开发工作 2&#xff09;Launcher是什么开发工作 3&#xff09;Settings是什么开发工作 4&#xff09;多…

使用UDP实现TCP的功能,会带来什么好处?

比较孤陋寡闻&#xff0c;只知道QUIC TCPQUIC握手延迟TCP需要三次握手TLS握手三次握手TLS握手放在一起&#xff0c;实现0RTT头阻塞问题TCP丢失保文&#xff0c;会影响所有的应用数据包基于UDP封装传输层Stream&#xff0c;Stream内部保序&#xff0c;Stream之间不存在相互影响…

实时智能应答3D数字人搭建2

先看效果&#xff1a; 3d数字人讲黑洞 根据艾媒咨询数据&#xff0c;2021年&#xff0c;中国虚拟人核心产业规模达到62.2亿元&#xff0c;带动市场规模达到1074.9亿元&#xff1b;2025年&#xff0c;这一数据预计将达到480.6亿元与6402.7亿元&#xff0c;同比增长迅猛。数字人可…

C语言:指针详解(1)

目录 一、内存和地址 1.内存 2.究竟该如何理解编址 二、指针变量和地址 1.取地址操作符(&) 2.解引用操作符(*) 3.指针变量的大小 三、指针变量类型的意义 1.指针的解引用 2.指针-整数 3.void*指针 四、const修饰指针 1.const修饰变量 2.const修饰指针变量 五…

C语言 | Leetcode C语言题解之第19题删除链表的倒数第N个结点

题目&#xff1a; 题解&#xff1a; struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {struct ListNode* dummy malloc(sizeof(struct ListNode));dummy->val 0, dummy->next head;struct ListNode* first head;struct ListNode* second dummy;f…

CSS核心样式-02-盒模型属性及扩展应用

目录 三、盒模型属性 常见盒模型区域 盒模型图 盒模型五大属性 1. 宽度 width 2. 高度 height 3. 内边距 padding 四值法 三值法 二值法 单值法 案例 4. 边框 border 按照属性值的类型划分为三个单一属性 ①线宽 border-width ②线型 border-style ③边框颜色 bo…

网页端HTML使用MQTTJs订阅RabbitMQ数据

最近在做一个公司的日志组件时有一个问题难住了我。今天问题终于解决了。由于在解决问题中&#xff0c;在网上也查了很多资料都没有一个完整的实例可以参考。所以本着无私分享的目的记录一下完整的解决过程和实例。 需求&#xff1a;做一个统一日志系统可以查看日志列表和一个可…

数据——关键生产要素

数据作为数字经济时代的关键生产要素&#xff0c;逐步融入生产生活各方面&#xff0c;深刻影响并重构着经济社会运行和社会治理&#xff0c;已成为影响未来发展的关键战略性资源。近年来&#xff0c;我国高度重视发展数字经济、数据要素及其市场化配置改革&#xff0c;发布了一…

Go——网络编程

一. 互联网协议介绍 网络基础——网络传输基本流程_网络传输过程-CSDN博客 应用层HTTP协议-CSDN博客 传输层UDP/TCP协议_udp报文提供的确认号用于接收方跟发送方确认-CSDN博客 网络层IP协议-CSDN博客 链路层以太网详解_以太网数据链路层-CSDN博客 二. Socket编程 Socket是…

vite+react+ts+scss 创建项目

npm create vitelatest输入项目名称选择react选择typescript swc WC 通过利用 Rust 编写的编译器&#xff0c;使用了更先进的优化技术&#xff0c;使得它在处理 TypeScript 代码时能够更快地进行转换和编译。特别是在大型项目中&#xff0c;SWC 相对于传统的 TypeScript 编译器…

Hive的分区与排序

一、Hive分区 1.引入&#xff1a; 在大数据中&#xff0c;最常见的一种思想就是分治&#xff0c;我们可以把大的文件切割划分成一个个的小的文件&#xff0c;这样每次操作一个个小的文件就会很容易了&#xff0c;同样的道理&#xff0c;在hive当中也是支持这种思想的&#xff…