Linux 工具

为了可以在 Linux 下能够进行 C/C++ 程序的开发,需要学习一些工具(工具其实也是指令)

一、软件包管理:yum

手机上的应用商店的软件,其实是在远端的服务器上,并不在本地的手机上,应用商店是通过网络在远端的服务器上下载的,我们可以把东西传到服务器上,也可以从服务器拿下来

例如:我们买的云服务器不是在本地的,而是在远端,我们是通过远程登录工具 xshell 来登录的

yum 类似于手机上的应用商店,帮助我们从远端的服务器上下载安装需要的软件包

1. 软件的生态环境

手机上为什么有人会给我们提供各式各样的软件?,因为这些公司想赚我们的注意力,从而通过广告等各种方式来盈利

那为什么会有人花钱提供并维护远端的服务器,并且还会有人无偿提供 Linux 下的软件呢?
与手机和 Windows 不一样的是,Linux 下都没有图形化界面,也就不能通过广告来盈利,而开源的软件一般都会成立自己的社区,这些开源组织成立的社区,是乐于捐赠的,那为什么会有人捐赠呢?因为软件开源了,所以软件稳定性,安全性等都很好,并且用这些软件的成本低,也就使得用这个软件的企业越来越多,那这些企业就不希望这个软件不维护了,所以这些企业就一定会给这些社区捐钱,捐设备等

其实也就是因为企业再用,企业会给开源社区捐钱,因此我们便可以跟着免费用这个开源软件

开源的生态:开源方便传播

2. yum 的使用

yum 的使用一般都建议使用 root 账户,因为安装或卸载软件时,需要更改系统目录中的内容,防止没有权限

  • sudo yum list | grep ‘软件名’,功能:查找软件
    搜索出来的内容
    软件名.cpu架构 主版本号.次版本号.源程序发行号-软件包的发行号.主机平台 软件源(类似于进入游戏的某某工作室),其中 el 是 centos 的简写
  • sudo yum -y install 软件,功能:安装软件
    Linux 还有一种安装软件的方法,下载软件的源代码,然后编译即可
  • sudo yum -y remove 软件,功能:删除软件

-y 选项表示不要询问

3. yum 源及分类

一般使用的软件不会是特别新的,因为新的软件虽然解决了一些曾经的问题,但肯定会带来一些新的问题,这些新的问题还没有完全暴露出来,但是旧的软件的大部分问题以及暴露出来了

因此软件会被分成 官方软件 和 扩展软件,新的软件刚诞生时,是不能进入官方软件的(软件可能比较强大,但是也存在许多问题),而是被放到扩展软件中,当人们用了两三年之后,发现这个软件写的很好,才会纳入官方软件中

远端的服务器有成百上千,应用商店是怎么知道要到哪里下载?

  • 应用商店一般都会内置下载地址(写死的,或者配置文件),yum 也有自己的配置文件称作 yum 源(/etc/yum.repos.d/),yum 在下载时就会根据配置文件中的地址,去下载对应的软件
  • 在我们平常下软件的时候,会去找对应的官网,搜索软件的下载地址,而这些搜索的动作应用商店会自动帮我们做

由于软件被分为官方软件和扩展软件,同样 yum 下载的软件也是分为官方软件和扩展软件的

  • 基础(官方) yum 源:CentOS-Base.repo,下载官方软件的地址
  • 扩展 yum 源:epel.repo,下载扩展软件的地址
    如果想安装的软件找不到,有可能在扩展 yum 源中

如果没有扩展 yum 源:sudo yum -y install epel-release ,根据官方 yum 源,找到和他匹配的扩展 yum 源

4. 在 centos 7.6 下更新 yum 源到国内镜像

  1. 先把 CentOS-Base.repo mv(备份一下),如果配置失败,以便进行恢复
  2. 百度找下载文件 wget yum 源,可以通过网络搜索 yum 源,因为 centos 7 是非常成熟的平台了
  3. 把下载的 yum 源重命名为 CentOS-Base.repo 这个名字
  4. 清空缓存 yum clean all
  5. 生成缓存 yum makeache 这里可以看到新的 yum 源网址

wget url
功能:远程从 url 获取资源

5. yum 命令

yum [options] [command] [package …]

options:可选

  • -h 帮助
  • -q 不显示安装过程

command:要进行的操作

  • check-update:列出所有可更新的软件清单
  • update:更新所有软件
  • install <package_name>:仅安装指定的软件
  • update <package_name>:仅更新指定的软件
  • list:列出所有可安装的软件清单
  • remove <package_name>:删除指定的软件
  • search <keyword>:查找指定关键字的软件
  • clean packages: 清除缓存目录下的软件包
  • clean headers: 清除缓存目录下的 headers
  • clean oldheaders: 清除缓存目录下旧的 headers
  • clean,clean all (= yum clean packages; yum clean oldheaders) :清除缓存目录下的软件包及旧的 headers

package:安装的包名

二、编辑器:vim

vim 可以满足我们在 Linux 下的常规开发,还可以处理在 Linux 出现的紧急情况, 当我们将一个写好的程序在 Linux 环境下运行起来时,发现存在问题,这时就可以使用 vim 在 Linux 的环境下对文件进行修改

vi 是 vim 的前身,vim 是 vi 的升级版本,兼容 vi 的所有指令,而且还有一些新的特性,vim 是一种多模式的编辑器,其中最常用的 5 种模式:命令模式、底行模式、插入模式、替换模式、视图模式

vim 回车:确认是否安装了 vim,没有安装则使用 sudo yum -y install vim

vim 文件:打开文件,如果文件不存在 vim 会自动创建,打开文件后就不要用鼠标操作了

1. 命令模式

vim 打开文件后默认在命令模式,只能由命令模式进入其他模式,Esc 键即可从其他模式回到命令模式

  k		  上
h   l	左	右
  j		  下

命令模式下的指令基本上都可以在前面加上数字,代表重复多少次的意思
命令模式下的指令:

  • h:光标左移一格
  • j:光标下移一格
  • k:光标上移一格
  • l:光标右移一格
  • shift + 6 [^] :光标移动到行首 (这个加数字没用)
  • shift + 4 [$] :光标移动到行尾
  • gg:光标移动到文本开头
  • shift + g [G] :光标移动到文本末尾
  • 数字后回车:光标向下移动 n 行
  • b:光标移动到上个字(连续的字母(包括空白字符),连续的符号(包括空白字符,不包含字母))的开头
  • w:光标移动到下个字(连续的字母,连续的符号(不包含字母))的开头
  • e:光标移动到下个字(连续的字母,连续的符号(不包含字母))的字尾
  • ctrl + f:上滑一页
  • ctrl + b:下拉一页
  • ctrl + d:上滑半页
  • ctrl + u:下拉半页
  • x:删除光标所在位置的字符
  • shift + x [X] :删除光标之前的字符
  • dw:剪切光标所在的位置到字尾的字符
  • d(shift + 6) [d^] :剪切光标之前到行首的字符(不包括光标字符)
  • d(shift + 4) [d$] :剪切光标的字符到行尾的字符(包括光标字符)
  • dd:剪切光标所在的行到缓冲区
  • yw:复制光标所在的位置到字尾的字符到缓冲区
  • y(shift + 6) [y^] :复制光标之前到行首的字符到缓冲区(不包括光标字符)
  • y(shift + 4) [y$] :复制光标的字符到行尾的字符到缓冲区(包括光标字符)
  • yy:复制光标所在行到缓冲区
  • p:如果之前复制的是字,则粘贴缓冲区的内容到光标之后,并且光标的位置移动到所粘贴的字的最后一个字符处,如果之前是剪切或者复制的是行,则粘贴缓冲区的内容到当前行的下面,并且光标移动到所粘贴的行的开头
  • shift + p [P] :如果之前复制的是字,则粘贴缓冲区的内容到光标之前,并且光标的位置移动到所粘贴的字的最后一个字符处,如果之前是剪切或者复制的是行,则粘贴缓冲区的内容到当前行(相当于粘贴到当前行的上面),并且光标移动到所粘贴的行的开头
  • shift + ` [~] :快速进行大小写转换
  • r:替换光标所在处的字符
  • shift + r [R] :替换光标所到之处的字符,按下 Esc 结束替换(替换模式)
  • u:撤销最近一次对内容的操作(包括进入其他模式对内容做出的修改),即使保存了文件,也可以撤销最近一次对内容的操作
  • ctrl + r:重做
  • /字符串:在文档中高亮显示匹配的字符串,搜索后 n 表示从光标处向下查找,N 表示从光标处向上查找
  • ?字符串:在文档中高亮显示匹配的字符串,搜索后 n 表示从光标处向上查找,N 表示从光标处向下查找
  • . :重复上一次操作

2. 底行模式

shift + ; [:] 进入底行模式,在底行模式中,执行一条指令后,会回到命令模式

底行模式下的指令:

  • w:保存
  • q:退出
  • wq:保存并退出
  • w!:强制写入(其实就是把源文件删了,然后新建一个文件,拷贝内容而已,有目录的 w 权限即可)
  • q!:不保存,强制退出
  • wq!:强制写入并退出(其实就是把源文件删了,然后新建一个文件,拷贝内容而已,有目录的 w 权限即可,有些文件比较重要,即使你有权限,写入退出时也会提醒你)
  • 数字:跳转到指定行号
  • noh:取消高亮显示(如:查找内容的结果会被高亮显示)
  • set nu:显示行号
  • set nonu:关闭行号
  • s/old/new:替换当前行匹配到的第一个 old 为 new
  • s/old/new/g:替换当前行匹配到的所有 old 为 new
  • %s/old/new:替换文档中匹配到的所有行的第一个 old 为 new
  • %s/old/new/g:替换文档中匹配到的所有行的所有 old 为 new
    s 前面可以加上两个数字(数字a,数字b),表示替换的行为 a 行到 b 行,1,$ 和 % 都表示全文
    g 是选项表示整行替换,c 是替换前询问,i 表示不区分大小写,带选项时可以 gi
  • !命令行指令:不退出 vim 执行命令行指令(执行命令行指令后,按任意键回到 vim)
  • vs 文件名:对比文件,vim 分屏操作(ctrl + ww 光标切换到不同的文件,文件如果不存在,在 vim 中对该文件保存退出时会自动创建)

Esc 回到命令模式

3. 插入模式

默认是在光标之前开始输入的

从命令模式进入插入模式:

  • i:在光标之前开始输入(光标不动)
  • shift + i [I]:在光标所在行的第一个非空白字符之前开始输入(光标移到第一个非空白字符)
  • a:在光标之后开始输入(光标往后一格)
  • shift + a [A]:在光标所在行的最后一个字符(包括空白字符)之后输入字符(光标移到最后一个字符的后一格)
  • o:在光标所在行下面新起一行(光标移到新起行的开头)
  • shift + o [O]:在光标所在行上面新起一行(光标移到新起行的开头)

Esc 回到命令模式时,光标会往前一格,因为输入是在光标之前开始输入的,如果在一行输入到末尾时,结束时,光标字符可能不存在

4. 替换模式和视图模式

  • 替换模式:shift + r [R],替换光标所到之处的字符,按下 Esc 结束替换
  • 视图模式:
    批量化注释:ctrl v,hjkl 选中区域,然后 shift + i [I],然后//,最后 Esc
    取消注释:ctrl v,hjkl 选中区域,然后 d

5. vim 配置

vim 在启动的时候,会自动在当前用户的目录下寻找 vim 的配置文件(.vimrc),如果没有配置文件,则 vim 就是默认的配置

Linux 中软件和程序只安装一份,这样所有用户都可以使用,对于 vim 的配置文件,每个用户可以自己配置,不影响其他用户,不想要配置文件时,直接删除即可

  • 自己配置(太麻烦了):需要在家目录下创建一个 .vimrc 配置文件,然后在网上查找 vim 配置选项,在配置文件中输入对应的选项或字段即可,如果选项不想要了,在选项前加上一个“(代表注释)
    在 vim 配置文件中的内容是及时生效的,重新打开 vim 就可以看到效果

  • 自动化配置:gitee 上搜索 vimforcpp(目前只支持 centos 7.x)
    在自己的用户下执行指令 curl -sLf https://gitee.com/HGtz2222/VimForCpp/raw/master/install.sh -o ./install.sh && bash ./install.sh 即可(不推荐在 root 下执行)
    卸载自动化配置:,在自动化配置的用户下执行指令 bash ~/.VimForCpp/uninstall.sh

三、编译器:gcc/g++

gcc 和 g++ 的选项基本上是一模一样的,gcc 用来编译 C语言,g++ 用来编译 C++

sudo yum -y install gcc 安装 gcc,sudo yum -y install gcc-c++ 安装 g++

编译器是用来将文本文件翻译成二进制可执行文件的

1. C语言程序的翻译过程

  1. 预处理:对 C语言的源代码文件,进行头文件展开,条件编译,宏替换,去注释等
  2. 编译:将预处理过后干净的 C语言代码翻译成汇编语言
  3. 汇编:将编译后生成的汇编代码翻译成可重定位目标二进制文件(不可以被执行),后缀.obj
  4. 链接:将我们自己形成的 .obj 文件和库文件进行某种合并(链接),形成可执行程序
  • 在预处理阶段,头文件会被拷贝到 .c 文件中,所以2 - 3 过程就不会对头文件进行编译
  • 编译和汇编阶段,都只翻译自己写的 .c 为后缀的代码,有几个 .c 文件,就会生成几个 .obj 文件,链接之前处理的都是自己写的代码
  • 链接阶段,将自己的代码和库文件中的代码链接起来(在 C语言代码中调用的库函数,并不是我们自己写的,但是我们可以用),生成一个可执行文件(即使没有使用库中的函数,也需要经过链接,因为汇编生成的 .obj 文件并不可以执行(不是完整的 ELF 格式))

选项合起来 ESc、后缀合起来 iso(镜像文件后缀),这样可以方便记忆

  • gcc -o 文件 -E 源文件,预处理做完就停下来,-o 后面紧跟目标文件名,一般后缀为.i
  • gcc -o 文件 -S 文件,编译做完就停下来,-o 后面紧跟目标文件名,一般后缀为.s
  • gcc -o 文件 -c 文件,汇编做完停下来,-o 后面紧跟目标文件名,一般后缀为.o,虽然是二进制文件,但是不能运行
  • gcc -o 文件 文件,生成可执行文件,-o 后面紧跟目标文件名,不加 -o 选项,默认生成 a.out 可执行文件
    编译器会自动识别代码的语言,然后自动去找对应的库文件进行链接,所以链接阶段不用加选项

对于第三方库文件,我们在写 C/C++代码时一直在使用(调用语言库文件中的函数),所以对于第三方库文件(除了语言库文件还有其他的库文件)我们是离不开的

Linux 系统中 C语言头文件(/usr/include),Linux 系统下 C语言标准库(/lib64/libc.so.6->/libc-2.17.so)

2. 链接方式和库文件

链接方式分为动态链接和静态链接

ldd 可执行文件
功能:显示该文件生成的过程中依赖的动态库文件

[admin@iZ2vcer6gtjgqa43cdpeeaZ ~]$ ldd test
	linux-vdso.so.1 =>  (0x00007fff02b98000)
	libc.so.6 => /lib64/libc.so.6 (0x00007f442ed5e000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f442f12b000)

常识:为了代码可以编译成可执行程序,系统中必须携带语言级别的头文件和语言对应的库
在安装 vs2022 的时候,最重要的一个工作也就是下载安装语言对应的头文件和库文件

常识:对于翻译型语言,库分两种(本质也是文件):

  • Linux 下静态库的名称 libxxxx.a,Windows 下静态库的名称 xxxx.lib
  • Linux 下动态库的名称 libxxx.so,Windows 下动态库的名称 xxxx.dll

在 Linux 下去掉前缀 lib 和 后缀 .a/.so 就是库的名称

我们用的相当一部分指令都是 C语言写的,通过 ldd /usr/bin/ls 即可看到依赖了 C标准库

如何看待指令呢?指令==程序==工具,他们都是一样的(都是 C语言写的,都是经过编译形成的,都和 C标准库有关)

//代码中引用的头文件一方面告诉了程序员该文件有什么方法可以调用,另一方面告诉编译器库函数所在的位置

在代码中总有一些功能我自己是无法独立完成的,比如需要和硬件交互的IO函数等,这些需要我们了解操作系统的知识,通过系统调用接口来实现,就算我们了解了操作系统的知识,自己写,难免会踩坑,而别人的库大多数坑已经暴露了,因此使用别人的库,便可以提高开发效率

动态库(存放可执行的库函数代码的文件):为了让用户的程序可以动态链接

动态链接:把在动态库中需要的库函数的地址拷贝自己的程序中(程序运行时需要跳转到动态库中执行库函数),动态链接成功后,程序运行时依赖动态库

任何程序都可以从动态库中拷贝需要的库函数地址到自己的程序,所以动态库是被共享的,动态库也被称为共享库

由于动态库中保存的都是库函数的代码,所以动态库文件具有只读属性,不能修改的,使用时,通过链接时拷贝的地址,直接到库中调用即可

  • 动态链接的优点:动态库中的代码,可以做到被程序共享,实现的代码永远都是在库中,动态链接的程序只需拷贝库函数的地址即可,因此比较节省空间
  • 动态库的缺点:如果动态库被删了,依赖动态库的程序就无法运行

很多程序编译链接好后,都从依赖的动态库中拷贝了所调用的函数的地址,运行时便通过地址跳转到动态库中执行函数,如果动态库没了,所有依赖这个库的程序也就无法正常使用了

很多语言都是 C语言写的,很多程序都依赖于 C标准动态库,如果 C标准动态库被删了,系统中的绝大多数指令,就无法运行了

以前听说的在可执行程序中的符号表,要么保存的是数据,要么保存的就是函数地址

在 Linux 中默认采用的就是动态链接的方式

静态库(存放可执行的库函数代码的文件):为了让用户的程序可以静态链接

静态链接:把在静态库中需要的库函数的代码拷贝到自己的程序中(程序运行时无需跳转),静态链接成功后,也就不依赖静态库了

  • 静态库的缺点:由于静态链接会拷贝代码到我的程序中,导致我的代码增多,如果很多程序,都采用静态链接,静态库的代码就会被拷贝很多份,就会占用我们的资源,放在磁盘里,浪费磁盘空间,加载到内存中,浪费内存空间

静态链接生成的可执行文件的大小,比动态链接生成的可执行文件大小大很多(正常情况下,相差了 120 倍)

  • 静态库的优点:程序在静态链接时已经将静态库的代码拷贝到了自己的程序中,静态库即使被删了,静态链接的程序任然可以正常运行

在 gcc 中 加上 -static 选项,即可采用静态链接的方式

一般的云服务器,默认只安装了动态库,sudo yum -y install glibc-static 安装 C语言静态库,sudo yum -y install libstdc++-static 安装 C++静态库

file 可执行文件
功能:可以查看文件的链接方式

dynamic link 动态链接,static link 静态链接

[admin@iZ2vcer6gtjgqa43cdpeeaZ ~]$ ll
total 4
-rw-rw-r-- 1 admin admin 210 Jan 12 17:56 test.c
[admin@iZ2vcer6gtjgqa43cdpeeaZ ~]$ gcc -o test_dynamic test.c
[admin@iZ2vcer6gtjgqa43cdpeeaZ ~]$ ll
total 16
-rw-rw-r-- 1 admin admin  210 Jan 12 17:56 test.c
-rwxrwxr-x 1 admin admin 8496 Jan 12 18:00 test_dynamic
[admin@iZ2vcer6gtjgqa43cdpeeaZ ~]$ file test_dynamic 
test_dynamic: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=cfbf492c2da1edde1b484f4b2410bb33fcdb1f8e, not stripped

[admin@iZ2vcer6gtjgqa43cdpeeaZ ~]$ ll
total 16
-rw-rw-r-- 1 admin admin  210 Jan 12 17:56 test.c
-rwxrwxr-x 1 admin admin 8496 Jan 12 18:00 test_dynamic
[admin@iZ2vcer6gtjgqa43cdpeeaZ ~]$ gcc -o test_static test.c -static
[admin@iZ2vcer6gtjgqa43cdpeeaZ ~]$ ll
total 860
-rw-rw-r-- 1 admin admin    210 Jan 12 17:56 test.c
-rwxrwxr-x 1 admin admin   8496 Jan 12 18:00 test_dynamic
-rwxrwxr-x 1 admin admin 861432 Jan 12 18:16 test_static
[admin@iZ2vcer6gtjgqa43cdpeeaZ ~]$ file test_static
test_static: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.32, BuildID[sha1]=2b55b0963638034b837eca52dfe74ef60c8b9deb, not stripped

-std=c99 表示支持 C99 标准

四、调试器:gdb

调试器主要是为了帮助程序员找到 bug 的位置,定位问题

sudo yum -y install gdb 安装 gdb

在 Linux 中用 gcc/g++ 默认编译形成的是 release 版本的可执行程序,需要在 gcc/g++ 编译时,指定 -g 选项,才能生成 debug 版本的可执行程序

debug 是调试版本,会添加调试信息,供程序员调试,release 是发布版本,供用户使用的,不会添加调试信息,因为用户不会去调试代码,并且不加调试信息还可以减小可执行程序的体积,方便用户下载

readelf -S 可执行文件 读取可执行文件的二进制构成,Linux 可执行文件遵守 ELF 格式,其中如果含有 debug 就代表包含调试信息

gdb 可执行文件 进入调试,ctrl + d 或者 q [quit] 退出调试,gdb 支持 tab 命令自动补全

  • l [list] 文件:行号/函数名:列出指定文件中,指定行号往下的源代码/指定函数的源代码(函数在中心位置),未指定时接着上次的位置往下列,每次列 10 行
  • r [run] :运行程序,遇到断点会停止
  • b [break] 行号/函数名:在某一行设置断点/某个函数开头设置断点
  • disable breakpoints(可以省略) 断点编号n:禁用编号为 n 的断点
  • enable breakpoints(可以省略) 断点编号n:启用编号为 n 的断点
  • d [delete] breakpoints(可以省略) 断点编号n:删除编号为 n 的断点
  • d [delete] breakpoints(可以省略):删除所有断点
  • i [info] b [break] :查看断点信息,Num(断点编号),Type(类型),Disp(断点配置),Enb(断点是否可用),Address(断点地址),What(断点位置)
  • n [next] :逐过程(vs:f10)
  • s [step] :逐语句(vs:f11)
  • p [print] 变量:打印变量的值,只显示一次
  • display 变量:跟踪查看一个变量,每次停下来都显示他的值(长显示),变量可以是内置类型,结构体类型,stl 等
  • undisplay 变量编号n:取消编号为 n 的变量的跟踪(取消长显示)
  • until 行号:在函数内,进行指定行号跳转,执行完区间代码,可以用于跑完循环
  • finish:在函数内,只执行完该函数,就停下来
  • c [continue] :从一个断点处,运行到下一个断点处
  • start:开始执行程序,停在 main 函数第一行语句前面
  • set var 变量=xxx:设置某一个变量为特定值,如:设置循环变量
  • bt [breaktrace] :查看函数的调用过程以及参数
  • 回车:执行最近的一条语句

五、自动化构建 make/Makefile

1. make/Makefile

make/Makefile 其实挺复杂的,目前只需要写一些简单的就好

在编译源文件时,如果指令写错,可能会导致源文件被清空(gcc -o test.c test),删除编译产生的文件时,如果指令写错,可能导致源文件被删了

为了避免频繁的写 gcc 指令和降低风险,所以想实现一定程度的自动化编译和自动化清理,因此可以使用自动化构建工具 make/Makefile

make 是一个命令,Makefile 是一个文件,需要自己在当前源代码的目录下创建

Makefile 是一个围绕依赖关系和依赖方法构建的一个自动化构建工具,完成一件事情,必须得有正确的依赖关系和正确的依赖方法(世界的运转规则就是这样)

简单的 Makefile 内容如下:

test:test.c
	gcc -o test test.c
.PHONY:clean
clean:
	rm test
  • 先写依赖关系:需要生成的目标文件 test 依赖于 test.c(依赖文件可以有 0 个或多个(用空格隔开),也称作依赖文件列表)
  • 然后写依赖方法(需要以 tab 开头):test.c 生成 test 的方法
    .PHONY:目标:表示依赖关系对应的依赖方法总是被执行的

Makefile 中写好内容后,在命令行中输入 make,make 会自动查找当前目录下的 Makefile 文件,然后从 Makefile 文件中的第一个依赖关系开始解析,如果依赖关系的依赖的对象不存在,则会继续向下扫描依赖对象的依赖关系的依赖对象,直到依赖对象存在,便执行对应的依赖方法,然后回退到上一个依赖关系,默认是执行第一个依赖关系,也可以在命令行中指定依赖关系,如 make test,make clean

当我们写好源代码时,make 即可生成可执行文件,make clean 即可清理掉可执行文件

[admin@iZ2vcer6gtjgqa43cdpeeaZ ~]$ ll
total 8
-rw-rw-r-- 1 admin admin  61 Jan 12 19:01 Makefile
-rw-rw-r-- 1 admin admin 210 Jan 12 17:56 test.c                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
[admin@iZ2vcer6gtjgqa43cdpeeaZ ~]$ cat Makefile 
test:test.c
	gcc -o test test.c
.PHONY:clean
clean:
	rm test
[admin@iZ2vcer6gtjgqa43cdpeeaZ ~]$ make
gcc -o test test.c
[admin@iZ2vcer6gtjgqa43cdpeeaZ ~]$ ll
total 20
-rw-rw-r-- 1 admin admin   61 Jan 12 19:01 Makefile
-rwxrwxr-x 1 admin admin 8536 Jan 12 19:01 test
-rw-rw-r-- 1 admin admin  210 Jan 12 17:56 test.c
[admin@iZ2vcer6gtjgqa43cdpeeaZ ~]$ ./test
110 100
PRINT
[admin@iZ2vcer6gtjgqa43cdpeeaZ ~]$ ll
total 20
-rw-rw-r-- 1 admin admin   61 Jan 12 19:01 Makefile
-rwxrwxr-x 1 admin admin 8536 Jan 12 19:01 test
-rw-rw-r-- 1 admin admin  210 Jan 12 17:56 test.c
[admin@iZ2vcer6gtjgqa43cdpeeaZ ~]$ make clean
rm test
[admin@iZ2vcer6gtjgqa43cdpeeaZ ~]$ ll
total 8
-rw-rw-r-- 1 admin admin  61 Jan 12 19:01 Makefile
-rw-rw-r-- 1 admin admin 210 Jan 12 17:56 test.c

在生成目标文件的依赖关系中,当我们执行 make 后,再执行 make(这里指 make test),便会提示 make: `test’ is up to date.test(test 是最新的了),这种情况称作不是总是执行的,如果想让 make 不报错,也就是 make(这里指 make test) 总是被执行,可以在依赖关系上面加上 .PHONY:test

.PHONY 修饰的目标被称作伪目标(因为总是被执行的,也就代表执行该依赖关系时,目标总是会被替换成新的)

在开发中源文件的时间,一定是早于可执行文件的,并且修改文件的内容后,会自动更新文件的时间,而 make 是根据这一原理,通过依赖文件的最近修改时间和目标文件的创建时间来判断目标文件是否是最新的,如果依赖文件的最近修改时间小于目标文件的创建时间,也就代表目标文件是最新的

在 vs 中,有时我们修改了源代码,但是运行的结果却还是之前的源代码的结果,就是因为 vs 把我们修改了的源代码的文件时间任然识别成了老的文件(vs 的 bug),便直接运行了上一次生成的可执行文件,这时候清理一下解决方案,再重新生成便可以解决了

.PHONY 原理:根据 make 判断目标文件是否是最新的方式,我们可以用 touch 修改依赖文件的时间,来欺骗 make,以达到总是被执行

stat 文件
功能:查看文件的时间

touch 文件
功能:文件存在时,将文件的时间更新为最新

一般不会在编译文件的依赖关系加上 .PHONY,当我们源文件很多时,编译文件是一件非常花时间的事情

clean 虽然也被修饰为总是被执行的,但是如果没有要删除的文件,指令会报错,为什么还要用.PHONY 来修饰 clean 呢? 其实,想达到的目的是,你要我清理,我就执行清理,但是能不能清理成功就不知道了,重点是我执行了清理

2. 进度条

进度条样式
[################### ][num%][\]

  1. # 号从左到右不断递增
  2. num 表示 # 号的数量
  3. 最后一个字符在原地转圈圈

预备知识:

  • \r(回车)会使光标移动到本行的开头,下次写入时,便会覆盖当前行的内容(因为在命令行中会从光标所在的位置开始打印)
  • \n(换行) 只会将光标向下移动一行(但实际我们使用的 \n,却是回到下一行的开头,是因为在语言中,系统默认把 \n 这个动作解释成了 \n\r 或者 \r\n)
  • 在程序中输出的内容会被放到输出缓冲区中,并不会立即显示到显示屏上,因为缓冲区有自己的刷新策略,对于向显示屏输出内容的缓冲区,在程序结束时或者遇到 \n(行缓冲) 时,缓冲区的内容会刷新,然后显示到显示屏中,也可以用 fflush 函数主动刷新缓冲区
    在 Windows 中可能和 Linux 差别,因为 Windows 的图形化界面是实时刷新的

通过显示并覆盖,便可以实现进度条,其中 usleep 函数的单位是微秒

//Makefile
test:progress.c test.c
	gcc -o test progress.c test.c
.PHONY:clean
clean:
	rm test

//progress.h
#include <stdio.h>
#include <unistd.h>

#define C '='

void progress();

//progress.c
#include "progress.h"

void progress()
{
    char str[102] = {0};
    char* ch = "|/-\\"; 
    int i = 0;
    for(i = 0; i <= 100; ++i)
    {
		//C语言 printf 是可以输出彩色内容的
		//感兴趣的话可以百度一下 C语言输出颜色格式,改进进度条代码
        printf("[%-100s][%d%%][%c]\r", str, i, ch[i % 4]);
        fflush(stdout);
        usleep(100000);
        str[i] = C;
        if(i < 99) str[i + 1] = '>';
    }
    printf("\n");
}

//test.c
#include "progress.h"

int main()
{
    progress();
    return 0;
}

凡是向显示器打印的东西都是字符,printf 函数会把我们输出的数据转换为字符串,然后用 putc 一个字符一个字符的显示出来,如 printf(“%d”, 123); 输出的内容是 1 字符,2 字符,3 字符,所以键盘和显示器也称作字符设备

六、代码托管:git

git 是一个版本控制工具,git 很强大,除了版本管理之外,还有分支管理,版本回退,版本标签等,这些内容就能支持我们进行多人协作

版本管理是什么?
以写实验报告为例,当张三第一次写完时,去找老师询问报告是否合格,老师说这里需要改改,当张三改完之后,再次去询问老师是否合格,老师说还需要改改,再次改完之后,再去询问老师,老师说,这次改的没有以前的好,把你的第一个版本给我吧,由于张三每次都是在当前的版本上进行修改,导致张三没有以前的版本,这时候就很麻烦,后来张三想到了一个办法,我每次写完一个版本时,我就记录一下,写完一个就记录一个,当我想要以前的版本的时候,我就可以很轻松的拿到了(这就是版本管理),后来,由于张三版本管理做的很好,就想着推广一下,让别人也可以用到自己的版本管理,于是每天就会有很多人发自己的版本给张三记录,张三每天就只能 ctrl c,ctrl v,的进行版本管理,张三便想到了一个办法,把这个版本管理做成一个软件,客户端和服务端的软件,需要用版本管理的人,用这个软件上传到服务端自己的账号下就可以了,并且可以浏览自己上传的内容,后来想到客户端和服务器其实没必要写成不一样的,就将客户端和服务器改成是一样的,也就是如果用户可以直接选择是否上传自己的版本到远端,也可以用客户端的软件,在自己的本地进行版本管理,也可以上传到远端进行管理(防止本地电脑出现问题),后来这个软件用的人很多了,但是软件是纯命令行的,后来就有公司将服务端做成了可视化界面(网站),用浏览器便可以直接查看修改直接的文件,并将客户端也做成了双击就可以上传等,这种网站和客户端就本称作 gitee 和 github,gitee 和 github 这些平台,底层的相关技术就是采用 git 来实现的

1. git 命令行基本操作

  1. sudo yum -y install git 安装git
  2. 先在服务端创建远端仓库,模板建议添加 Readme 文件,分支模型(目前不选择)
  3. 将远端仓库的所有内容克隆到本地,复制以 https 的方式的远端仓库地址后,在命令行中 git clone 复制的地址,克隆成功后,会在当前文件夹下生成一个和远端仓库名一样的文件夹,如果不小心删掉了本地仓库(仓库名文件夹下的 .git),在克隆一次就好了
  4. 进入到生成的文件夹中(这个不是仓库,该文件下的 .git 文件才是仓库),在该目录下添加想要管理的文件,注意:虽然当前目录下有这个文件了,但是这个文件并没有被 .git 仓库管理
  5. git add . ( . 表示没有添加到仓库中的所有文件,也可以指定文件),把当前目录下指定文件添加到 git 仓库中进行管理,也就是拷贝到 .git 的某个文件中
  6. git commit -m “说明”,把修改记录提交到本地仓库中(提交之后就去不掉了),说明是用来描述本次提交做了什么,这个说明会被保存起来的(要重视),通过这个步骤后,就完成了在本地对版本进行管理
  7. git push 让本地仓库和远端仓库保持一致,为了可以多人开发,或者防止本地出现问题
  8. git rm 文件,然后进行 git 三板斧操作,删除仓库中的指定文件,最好不要直接删文件(rm 文件)
  9. git log 查看所有提交日志,日志中会存在提交人的账号和邮箱,可以方便别人联系你
  10. git status 查看本地和远端的不同

克隆到本地的仓库其实就是一个文件夹(.git),删除 .git 文件夹,也就代表删除了本地仓库

遇到问题时先找有没有现成的解决方案

评论 28
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值