【Linux】Linux 开发工具(vim、gcc/g++、make/Makefile)+【小程序:进度条】-- 详解

我们在 Windows 中编写 C/C++ 程序时,常用的 VS2019 是一个集成开发环境,包含了很多工具包。而在 Linux 下开发,大部分的情况下都是使用一个个独立的工具。比如:编写代码用 vim,编译代码用 gcc,调试代码用 gdb。

一、编辑器 - vim

为什么选择使用 vim 呢?

因为 vim 是所有 Linux 环境下自带的。

vi/vim 的区别简单点来说,它们都是多模式编辑器。不同的是 vim 是 vi 的升级版本,它不仅兼容 vi 的所有指令,而且还有一些新的特性在里面。例如语法加亮,可视化操作不仅可以在终端运行,也可以运行于 x window、 mac os、windows。这里统一选择按照 vim 来进行讲解。


1、vim 的基本概念

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

2、vim 的基本操作

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

$ vim test.c
不过要特别注意,刚进入 vim 编辑器时,是处于正常模式下的,你要切换到插入模式才能够输入文字。
(1)正常模式切换至插入模式,有以下 3 种方式
  • i 进入插入模式后,从当前光标所在位置开始输入文字。
  • a 进入插入模式后,从当前光标所在位置的下一个位置开始输入文字。
  • o 进入插入模式后,插入新的一行,从行首开始输入文字。

(2)插入模式切换至正常模式
  • ESC 键。

(3)正常模式切换至末行模式
  • 按下 shift + : ,其实就是输入 : 冒号。

(4)退出 vim 操作,在正常模式中输入 : 冒号进入底行模式,然后选择输入
  • w(保存当前文件)
  • wq(存盘并退出 vim)
  • q!(不存盘且强制退出 vim)


3、vim 正常模式命令集

(1)移动光标
  • vim 可以直接用键盘上的方向键来控制光标上下左右移动,但正规的 vim 是用小写英文字母 h、j、k、l 来分别控制光标向左、下、上、右移一格。
  • gg:进入到文本开始
  • shift+g / G:移动到文章的最后
  • 按 shift+4 / $:移动到光标所在行的 “行尾”
  • shift+6 / ^:移动到光标所在行的 “行首”
  • w:光标跳到下个字的开头(以单词为单位)。
  • 按 e:光标跳到下个字的字尾(以单词为单位)。
  • b:光标回到上个字的开头(以单词为单位)。
  • 按 #l:光标移到该行的第 # 个位置,如:5l、56l。
  • # + shift+g / G:光标移动到第 # 行
  • 按 #j / k 光标向下 / 上移动 # 行。
  • 按 ctrl+b :屏幕往 “后” 移动一页。
  • 按 ctrl+f :屏幕往 “前” 移动一页。
  • 按 ctrl+u :屏幕往 “后” 移动半页。
  • 按 ctrl+d :屏幕往 “前” 移动半页。

(2)删除文字
  • x:每按一次,删除光标所在位置的一个字符。(常用)
  • #x:例如,6x 表示删除光标所在位置的 “后面(包含自己在内)” 的 6 个字符。
  • shift+x / X:大写的 X,每按一次,删除光标所在位置的 “前面” 一个字符。
  • #X:例如,20X 表示删除光标所在位置的 “前面” 20 个字符。
  • dd删除(剪切)光标所在行。(常用)
  • #dd从光标所在行开始删除 # 行。

(3)复制文字
  • yw:将光标所在之处到字尾的字符复制到缓冲区中。
  • #yw:复制 # 个字到缓冲区。
  • yy复制光标所在行到缓冲区。(常用)
  • #yy:例如,6yy 表示拷贝从光标所在的行(包含自己在内) “往下数” 6 行文字。
  • p:将缓冲区内的字符粘贴到光标所在位置。注意:所有与 “y” 有关的复制命令都必须与 “p” 配合才能完成复制与粘贴功能。(常用)
  • #p:将缓冲区内的字符粘贴 # 份到光标所在位置。
  • yy+p复制粘贴。
  • dd+p剪切粘贴。

(4)替换操作
  • r替换光标所在处的字符(局部文本替换),支持 #r。(常用)
  • R:进入替换模式,替换光标所到之处的字符,直到按下 ESC 键为止(整体文本替换)。

(5)字母大小写转换
  • shift+~:先按下 shift 键,再按下波浪号 ~ 不要停,往后遇到的所有小写字母将被转成大写,所有大写字母将被转成小写。

(6)撤销上一次操作(常用)

u:如果您误执行一个命令,可以马上按下 u,回到上一个操作。按多次 “u” 可以执行多次恢复。
ctrl+r:撤销 u 操作,也就是撤销的恢复(反撤销)。


(7)更改操作

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


(8)跳至指定的行

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


4、vim 在末行模式中的命令集

在使用末行模式之前,请记住先按  ESC  键确定您已经处于正常模式,再按 : 冒号即可进入末行模式。
(1)列出行号
  • set nu: 输入 set nu 后,会在文件中的每一行前面列出行号。

(2)跳到文件中的某一行
  • #:#号表示一个数字,在冒号后输入一个数字,再按回车键就会跳到该行了,如输入数字 15 后再回车,就会跳到文章的第 15 行。

(3)查找字符
  • /关键字:先按 / 键,再输入您想寻找的字符,如果第一次找的关键字不是您想要的,可以一直按 n 会往后寻找到您要的关键字为止。(常用)
  • ?关键字:先按 ? 键,再输入您想寻找的字符,如果第一次找的关键字不是您想要的,可以一直按 n 会往前寻找到您要的关键字为止。

(4)批量化替换字符
  • %s/printf/cout/g(把文中所有 printf 替换成 cout,g --global 表示全局的意思)(常用

(5)查看函数手册
  • !man [选项] [函数名]按 q 退出手册)。(常用

(6)保存文件
w :在冒号输入字母 w 就可以将文件保存起来可以跟一个感叹号  w! 强制保存

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

(8)多文件多屏操作
如果想把 test.c 文件中的 10 行代码复制 test1.c 文件中,该如何操作呢?

vs test1.c(在 vim 中打开 test1.c 文件,左右分屏

再按 ctrl + ww 组合键可以切换文件(w 要按两下)。


(9)跑任何想跑的命令

格式:!命令(! 表示底行执行 bash 命令),比如:


5、 vim 中批量添加和删除注释

方法一块选择模式

批量添加注释:

  1. 进入 vim 编辑器,按 ctrl+v 进入块选择模式(visual block),然后移动光标选择要添加注释的行
  2. 再按 shift+i / I 键(大写字母),进入 Insert 插入模式输入你要插入的注释符(比如 //)。
  3. 最后按 ESC 键,你所选择的行就被注释上了。

批量删除注释:

  1. 同样按 ctrl+v 进入块选择模式选中要删除的行首的注释符号,注意 // 要选中两个。
  2. 选好之后按 d 键即可删除注释,ESC 保存退出。

方法二替换命令

在末行模式下,可以采用替换命令进行注释:

  1. 添加注释:起始行号, 结束行号 s/^/注释符/g(表示在 xx 到 xx 行加入注释符,^ 表示行首的意思),然后按下回车键,注释成功。
  2. 删除注释:起始行号, 结束行号 s/^注释符//g(表示取消 xx 到 xx 行行首的注释符),然后按下回车键,取消注释成功。

比如:


6、简单 vim 配置

(1)配置文件的位置
  • 在目录 /etc/ 下面,有个名为 vimrc 的文件,这是系统中公共的 vim 配置文件,对所有用户都有效。
  • 而在每个用户的主目录下,都可以自己建立私有的配置文件,命名为:.vimrc。例如,root 用户的 /root 目录下,通常已经存在一个 .vimrc 文件,如果不存在,则创建之。
  • 切换用户成为自己执行 su ,进入自己的主工作目录,执行 cd ~
  • 打开自己目录下的 .vimrc 文件,执行 vim .vimrc

(2)常用配置选项,用来测试
  • 设置语法高亮:syntax on
  • 显示行号:set nu
  • 设置缩进的空格数为 4:set shiftwidth=4

(3)使用插件
要配置好看的  vim ,原生的配置可能功能不全,可以选择安装插件来完善配置,保证用户是你要配置的用户。
【参考文档】
手把手教你把Vim改装成一个IDE编程环境(图文)_vim 打造成 ide-CSDN博客

https://github.com/wsdjeg/vim-galore-zh_cn


二、编译器 - gcc/g++

1、gcc/g++ 命令 & 程序编译

C/C++ 程序要运行,一般要经历以下步骤:

预处理(进行宏替换)--> 编译(生成汇编)--> 汇编(生成机器可识别代码)--> 链接(生成可执行文件或库文件)

Linux 下通过 gcc 命令完成 C 程序编译的过程,通过 g++ 命令完成 C++ 程序编译的过程:

gcc 命令格式gcc [选项] 要编译的文件 [选项] [目标文件](g++ 与之类似)


(1)gcc 选项 
  • -E 只激活预处理,不生成文件,你需要把输出内容重定向到一个 .i 输出文件里面。
  • -S 只进行预处理、编译阶段,并生成 .s 汇编文件,不进行汇编和链接。
  • -c 只进行预处理、编译、汇编阶段,并生成 .o 目标文件,不进行链接。
  • -o 指明要生成的文件,输出内容到一个输出文件中。
  • -static 此选项对生成的文件采用静态链接
  • -g 生成调试信息。GNU 调试器可利用该信息。
  • -shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统提供动态库。
  • -O0、-O1、-O2、-O3 编译器的优化选项的 4 个级别,-O0 表示没有优化,-O1 为缺省值,-O3 优化级别最高。
  • -w 不生成任何警告信息。
  • -Wall 生成所有警告信息。
提示 :gcc 选项记忆:esc,iso

(2)预处理(进行宏替换

预处理阶段会做的事:头文件展开、宏替换、条件编译、去掉注释等等。

预处理指令是以 # 号开头的代码行。

命令格式:gcc –E hello.c –o hello.i

  • 选项 -E,该选项的作用是让 gcc 在预处理结束后停止编译过程。
  • 选项 -o,是指目标文件,.i 文件为已经过预处理的 C 原始程序。

(3)编译(生成汇编)

编译阶段会做的事:语法检查(代码的规范性、是否有语法错误等),函数实例化,生成 .s 汇编文件。

命令格式:gcc –S hello.i –o hello.s

用户可以使用 -S 选项来进行查看,该选项只进行编译而不进行汇编,生成汇编代码。


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

汇编阶段会做的事:把编译阶段生成的 .s 汇编文件转成 .o 目标文件(二进制机器码)。

命令格式:gcc –c hello.s –o hello.o

用户可使用选项 -c 即可看到汇编代码已转化为 .o 的二进制目标代码。


(5)连接(生成可执行文件或库文件)

在成功编译之后,就进入了链接阶段。

命令格式:gcc hello.o –o hello

2、函数库

在 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 命令验证。

3、动态链接和静态链接

生成可执行程序的方式有两种:

(1)动态链接:链接动态库
  • 优点:不需要把相关库中的代码拷贝到可执行程序中,编译效率高,程序运行起来后,需要用到哪个库,再把哪个库加载到内存中,边运行边加载。

  • 缺点:万一有库丢失了,将直接导致程序无法正常运行

(2)静态链接:链接静态库
  • 优点:不依赖于任何的动态库,自己就可以独立运行

  • 缺点:占磁盘空间,占内存,把相关库中的代码完完全全拷贝到了可执行程序中。

Linux 下生成的可执行程序,默认是动态链接的,如何查看呢?
  • 使用 ldd [filename] 命令可查看可执行文件的库依赖关系

  • 使用 file [filename] 命令可以查看可执行文件的信息和类型

想要生成的可执行程序是静态链接的,该如何做呢? 
$ gcc test.c -o test_s -static


三、项目自动化构建工具 - make/Makefile

1、基本概念

(1)背景
  • 对于一个多文件的项目,在 VS 集成开发环境中,可以自动帮我们维护好多文件,我们只需要一键就可以完成对所有文件的编译,生成可执行程序。

  • 而在 Linux,项目的所有文件,都需要我们自己来维护,成本太高,所以要用到 make 和 Makefile 帮我们自动化维护。


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

2、基本使用

现在编写了一个 test.c 文件,需要编译文件生成可执行程序:

方式一:直接使用 gcc 命令

$ gcc test.c -o test

方式二:可以用 make 命令:想要使用 make 命令,需要创建一个 makefile 文件。

如何创建 makefile 文件呢?

(首要先了解依赖关系和依赖方法的相关知识)

⚪依赖关系和依赖方法
  • 依赖关系表明我依赖于谁
上述例子中的文件 test  依赖 test .o
test.o 依赖 test.s         test.s 依赖 test.i         test.i 依赖 test.c
  • 依赖方法指的是对应的那个方法如何生成我
gcc test.* - option test.*  就是与之对应的依赖关系。

比如上述例子,单文件项目,只有 test.c 一个文件:

目标文件 test 依赖于原始文件 test.c,但仅仅只有依赖关系是不能生成目标文件的。还需要有依赖方法,而 gcc test.c -o test 就是与之对应的依赖方法,表明如何生成目标文件 test。

编写 makefile 文件:

test:test.c            # 表明了一种依赖关系,目标文件 test 依赖于 test.c         
	gcc test.c -o test # 依赖方法,怎么用 test.c 生成目标文件 test(需要以tab键开头)
.PHONY:clean           # .PHONY —— "定义"伪目标:clean总是可以被执行的
clean:                 # 依赖项为空
	rm -rf test        # 清理可执行程序

编写完 makefile 文件后,使用 make 命令:

解释.PHONY 的作用:

一般不会把可执行程序 “定义” 成伪目标,因为每次编译都是有成本的,第一次编译好了,就不需要再编译了,除非文件有改动。一般把清理可执行程序 “定义” 成伪目标。

简化 makefile 文件:

test:test.c
	gcc $^ -o $@  # $^: 可执行程序所依赖的文件列表 $@: 目标文件
.PHONY:clean
clean:
	rm -rf test

多文件项目,有 test.h test.c main.c 三个文件

编写 makefile 文件:

test:test.c main.c  # 目标文件 test 依赖于 test.c 和 main.c
	gcc $^ -o $@    # $^: 可执行程序所依赖的文件列表 $@: 目标文件
.PHONY:clean
clean:
	rm -rf test
为什么没有把 .h 头文件加入进来呢?

编译代码时头文件会展开,把头文件中的代码拷贝到源文件中,所以找到头文件才是最重要的,找头文件通常有两种路径:当前路径、系统路径。

 【总结】

以后当遇到的项目变复杂了,文件多了,不用直接写 gcc 命令了,而是用 make/makefile 自动化构建项目。


3、原理

make 的推导过程图:

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

clean 项目清理:

  • 工程是需要被清理的。
  • 比如 clean,如果没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要 make 执行。即命令 make clean,以此来清除所有的目标文件,以便重新编译。
  • 一般会把 clean 设置为伪目标.PHONY 修饰。(伪目标的特性是:总是被执行的)。

4、补充

  1. makefile 文件保存了编译器链接器的参数选项,并且描述了所有源文件之间的关系。make 程序会读取 makefile 文件中的数据,然后根据规则调用编译器,汇编器,链接器产生最后的输出。
  2. Makefile 里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释
  • 显式规则说明了,如何生成一个或多个目标文件。
  • make 有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写 makefile,比如源文件与目标文件之间的时间关系判断之类。
  • 在 makefile 中可以定义变量,当 makefile 被执行时,其中的变量都会被扩展到相应的引用位置上,通常使用 $(var) 表示引用变量。
  • 文件指示包含在一个 makefile 中引用另一个 makefile,类似 C 语言中的 include。
  • 注释,makefile 中可以使用 # 在行首表示行注释。

四、小程序 —— 进度条

1、回车、换行和回车换行

  • 回车:用 \r 表示。回到当前行的最开始,如果此时写入数据,会依次往后覆盖掉当前行的数据。
  • 换行
  • 回车换行光标移动到下一行的最开始。

注意

  • C 语言中的 \n 表示:回车并换行。
  • 键盘上的 Enter 键表示:回车并换行。


2、行缓冲区概念

(1)这段代码在 Linux 中运行,会产生什么结果呢?

#include <stdio.h>
#include <unistd.h> //sleep() 

int main()
{
    printf("hello world!\n"); //有'\n'
    sleep(3);
    return 0;
}

运行结果:先打印出 hello world,然后休眠 5s,结束程序。


(2)这段代码在 Linux 中运行,会产生什么结果呢?

#include <stdio.h>
#include <unistd.h> //sleep()                                                     
int main()
{
    printf("hello world"); //没有'\n'
	sleep(5);
    return 0;
}

运行结果:先休眠了 5s,当 5s 结束后,才打印出 hello world,结束程序。

当 sleep(5); 执行的时候,printf("hello world"); 已经执行完了,但却没有先打印字符串,这是为什么呢?

printf("hello world"); 已经执行完了,但并不代表字符串就得显示出来。

那在执行 sleep(5); 期间,字符串在哪里呢?

缓冲区(本质就是一段内存空间,可以暂存临时数据,在合适的时候刷新出去)。


3、补充

刷新是什么?

把数据真正的写入磁盘、文件、显示器、网络等设备或文件中。

刷新策略:

  1. 直接刷新,不缓冲。
  2. 缓冲区写满,再刷新(称为全缓冲)。
  3. 碰到 ‘\n’ 就刷新,称为行刷新。(注:行刷新一般对应的设备是显示器)
  4. 强制刷新。

任何一个 C 程序,启动的时候,都会默认打开三个流(文件):

  • 标准输入 stdin、标准输出 stdout、错误 stderr(类型是 FILE* 文件指针类型)
  • 如果想要让数据在显示器上显示出来,需要向输出流 stdout 中写入数据。
回到前面的问题,为什么在执行 sleep 的时候,没有显示字符串呢?

因为我们想要把字符串显示到显示器上,显示器默认是行刷新,遇到 ‘\n’ 才刷新,而我们前面写的代码中,并没有 ‘\n’,所以 printf 执行完了没有刷新。

为了在 printf 执行完的时候,让字符串立马显示出来,需要进行强制刷新,把字符串尽快的写入显示器中。

强制刷新需要用到一个函数:

#include <stdio.h>
int fflush(FILE *stream); //把当前缓冲区的数据写入到流中

因为是让字符串在显示器上显示,所以我们需要传文件指针 File* stdout,代码如下: 

#include<stdio.h>
#include<unistd.h> //sleep()                                                     
int main()
{
	printf("hello world"); //没有'\n',字符串写入到了缓冲区中,但不会被立即刷新出来
    fflush(stdout);        //强制刷新,把当前缓冲区中的数据写入到输出流文件中
	sleep(5);
    return 0;
}

运行结果:先打印出 hello world,然后休眠 5s,结束程序。 


4、进度条代码

#include <stdio.h>  //fflush
#include <string.h> //memset
#include <unistd.h> //usleep

#define NUM 102 //101个字符+'\0'

int main()
{
    char bar[NUM];
    memset(bar, 0, sizeof(bar)); //把进度条清零

    //每次循环,让字符串内容多一个'#',这样进度条就跑起来了
    const char* lable = "|/-\\"; //两个\\表示'\',共4个字符
    int cnt = 0;
    while (cnt <= 100)
    {
        printf("[%-101s][%d%%] %c\r", bar, cnt, lable[i%4]); //每次打印进度条不需要换行,覆盖掉当前行的内容就行
        bar[cnt++] = '#';
        fflush(stdout); //强制刷新,把用户缓冲区的数据刷新出来
        usleep(30000); //为了能够看到进度条,休眠30000us
    }
    printf("\n");
    return 0;
}

效果如下: 


5、小程序 —— 倒计时

弄明白了回车的概念后,下面写一个倒计时的小程序。

/* countDown.c */
#include<stdio.h>  //fflush
#include<unistd.h> //sleep()

int main()
{
    int count = 9;
    while (count >= 0)
    {
        printf("%-2d\r", count); //数据写入缓冲区中,\r表示回车,从当前行的最开始写入
        fflush(stdout);          //强制刷新,把用户缓冲区的数据刷新出来
        count--;   
        sleep(1); //为了能够看到倒计时,休眠1s
    }
	return 0;
}

运行结果:

【扩展】

虽然这里 count 是整型,但实际上打印到显示器上,是一个个字符。比如 int count = 123456,占 4 字节,使用 printf 打印到显示器上,是 6 个字符,占 6 字节。

  • printf 格式化输出,实际上就是把这个内存级的整型数据转换成显示器可以显示的字符型的数据。
  • scanf 格式化输入,实际上就是把键盘敲下的一个个字符型的数据转换成了一个内存级的整型数据。

文件分为二进制文件和文本文件,二进位文件在内存中是什么样子,写到文件中也就是什么样子。而文本文件写入到设备(文件)中时,是需要做转换的,比如显示器设备(也是一种文件),显示器是给人看的,所以它一定不是二进制文件,而是文本文件,只要是文本文件,必须要将所要显示的数据转换成人所能识别的一个个的字符型数据。

所以键盘和显示器设备(文件),统称为字符设备,体现在输入时是字符,输出时是字符。

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

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

相关文章

异步编程Completablefuture使用详解----进阶篇

JDK版本&#xff1a;jdk17 IDEA版本&#xff1a;IntelliJ IDEA 2022.1.3 文章目录 前言一、异步任务的交互1.1 applyToEither1.2 acceptEither1.3 runAfterEither 二、get() 和 join() 区别三、ParallelStream VS CompletableFuture3.1 使用串行流执行并统计总耗时3.2 使用并行…

《幻兽帕鲁》开荒最强帕鲁推荐!轻松拿下各种BOSS 幻兽帕鲁爆火 幻兽帕鲁2月服务器费用7000万 幻兽帕鲁图鉴

最近一款叫做《幻兽帕鲁》的新游戏走红&#xff0c;成为了Steam游戏平台上&#xff0c;连续3周的销量冠军&#xff0c;有不少Mac电脑用户&#xff0c;利用CrossOver成功玩上了《幻兽帕鲁》&#xff0c;其实CrossOver已经支持很多3A游戏&#xff0c;包括《赛博朋克2077》《博德之…

Ps:自动对齐图层

Ps菜单&#xff1a;编辑/自动对齐图层 Edit/Auto-Align Layers 自动对齐图层 Auto-Align Layers命令通过分析选中图层上的图像&#xff0c;识别出图像间的共同特征点&#xff08;如边缘、纹理或特定标记等&#xff09;&#xff0c;然后基于这些特征点变换&#xff08;移动、旋转…

python 爬虫篇(2)---->re正则实战豆瓣读书爬取(附带源码)

re正则实战—豆瓣读书爬取 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 re正则实战---豆瓣读书爬取前言一、准备工具二、构建请求头三、请求数据四、解析数据五、保存数据总结(源码)前言 大家好,今天我们来写一个豆瓣读书的爬虫程序,我会只用…

ProcessSlot构建流程分析

ProcessorSlot ProcessorSlot构建流程 // com.alibaba.csp.sentinel.CtSph#lookProcessChain private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)throws BlockException {// 省略创建 Context 的代码// 黑盒…

Rust 第一个rust程序Hello Rust️

文章目录 前言一、vscode 安装rust相关插件二、Cargo New三、vscode调试rustLLDB 前言 Rust学习系列。今天就让我们掌握第一个rust程序。Hello Rust &#x1f980;️。 在上一篇文章我们在macOS成功安装了rust。 一、vscode 安装rust相关插件 以下是一些常用的 Rust 开发插件…

Axure RP 网页版,让原型设计更高效

交互神器Axure RP是一种专业的快速原型设计工具&#xff0c;但Axure在用户体验上的缺陷也很明显。其设置交互方式相对繁琐&#xff0c;可视化不足、条件判断、变量、中继器等功能的使用需要陡峭的学习曲线。许多设计师正在寻找一个可以取代Axure的原型设计工具&#xff0c;即时…

Python IDE——PyCharm的下载与安装(2024)

目录 一、Python开发工具 二、下载PyCharm 三、安装PyCharm 四、使用PyCharm 一、Python开发工具 Python解释器捆绑了Python的官方开发工具——IDLE(Integrated Development and Learning Environment&#xff0c;集成开发和学习环境)。IDLE具备集成开发环境&#xff08;I…

MySQL 小技巧:xtrabackup 软件包的下载及安装

案例&#xff1a;xtrabackup 软件包的下载及安装 软件包下载&#xff1a;Index of /percona/centos/7/RPMS/x86_64/ CentOS7 默认的数据库版本比较老,因此建议使用 xtrabackup 2.4 版本 // CentOS7 默认的数据库版本比较老,因此建议使用 xtrabackup 2.4 版本 // 安装 CentOS7 默…

vue3 之 组合式API—reactive和ref函数

ref&#xff08;&#xff09; 作用&#xff1a;接收简单类型或者对象类型的数据传入并返回一个响应式的对象 核心步骤&#xff1a; 1️⃣ 从 vue 包中导入 ref 函数 2️⃣在 <script setup>// 导入import { ref } from vue// 执行函数 传入参数 变量接收const count …

【考研408】计算机网络笔记

文章目录 计算机网络体系结构计算机网络概述计算机网络的组成计算机网络的功能计算机网络的分类计算机网络的性能指标课后习题 计算机网络体系结构与参考模型计算机网络协议、接口、服务的概念ISO/OSI参考模型和TCP/IP模型课后习题 物理层通信基础基本概念奈奎斯特定理与香农定…

Java二维数组的遍历

目录 创建二维数组二位数组初始化二位数组的遍历分析 创建二维数组 public class TestArray05{public static void main(String[] args){//定义一个二维数组&#xff1a;int[][] arr new int[3][];//本质上定义了一个一维数组&#xff0c;长度为3int[] a1 {1,2,3};arr[0] a…

java hutool工具类实现将数据下载到excel

通过hutool工具类&#xff0c;对于excel的操作变得非常简单&#xff0c;上篇介绍的是excel的上传&#xff0c;对excel的操作&#xff0c;核心代码只有一行。本篇的excel的下载&#xff0c;核心数据也不超过两行&#xff0c;简洁方便&#xff0c;特别适合当下的低代码操作。 下载…

vue2学习笔记(2/2)

vue2学习笔记&#xff08;1/2&#xff09; vue2学习笔记&#xff08;2/2&#xff09; 文章目录 1. 初始化脚手架2. 分析脚手架&render函数文件结构图示及说明main.jsindex.htmlApp.vueSchool.vueStudent.vue 关于不同版本的Vue修改默认配置vue.config.js配置文件 3. ref属…

百分点科技:《数据科学技术: 文本分析和知识图谱》

科技进步带来的便利已经渗透到工作生活的方方面面&#xff0c;ChatGPT的出现更是掀起了新一波的智能化浪潮&#xff0c;推动更多智能应用的涌现。这背后离不开一个朴素的逻辑&#xff0c;即对数据的收集、治理、建模、分析和应用&#xff0c;这便是数据科学所重点研究的对象——…

探索前端开发框架:React、Angular 和 Vue 的对决(二)

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

使用Ettus USRP X440对雷达和EW系统进行原型验证

概览 无论是保障己方平台的生存能力&#xff0c;还是扰乱敌方频谱使用&#xff0c;以电磁(EM)频谱为主导都是任务成功的主要因素。电磁频谱操作(Electromagnetic Spectrum Operation, EMSO)需要使用战术系统来监测敌方的频谱活动、定位其发射器并帮助己方制定行动计划。软件无…

问题:魁奈在税收政策方面的主张是() #学习方法#其他#经验分享

问题&#xff1a;魁奈在税收政策方面的主张是&#xff08;&#xff09; A.征收农业税 B.征收工商业税 C.征收间接税 D.征收地租税 参考答案如图所示

网络协议梳理

1 引言 在计算机网络中要做到有条不紊地交换数据&#xff0c;就必须遵守一些事先约定好的规则。这些规则明确规定了所交换的数据的格式以及有关的同步问题。这里所说的同步不是狭义的&#xff08;即同频或同频同相&#xff09;而是广义的&#xff0c;即在一定的条件下应当发生什…

自学网安-IIS服务器

部署环境&#xff1a;win2003 配置环境&#xff1a;winxp ip&#xff1a;10.1.1.2 win2003 ip&#xff1a;10.1.1.1 开始安装 双击“应用程序服务器” 双击“Internet 信息服务&#xff08;IIS&#xff09;” 勾选万维网服务&#xff0c;确定然后下一步 查看端口号;netstat …