基础的开发工具

学习⽬标
学习yum⼯具,进⾏软件安装
掌握vim编辑器使⽤,学会vim的简单配置
掌握gcc/g++编译器的使⽤,并了解其过程,原理
掌握简单的Makefile编写,了解其运⾏思想
编写⾃⼰的第⼀个Linux 程序:进度条
学习 git 命令⾏的简单操作, 能够将代码上传到 Github 上
掌握简单gdb使⽤于调试

一. 软件包管理器

 1-1 什么是软件包

        

在Linux下安装软件, ⼀个通常的办法是下载到程序的源代码, 并进⾏编译, 得到可执⾏程序.

但是这样太⿇烦了, 于是有些⼈把⼀些常⽤的软件提前编译好, 做成软件包(可以理解成windows上

的安装程序)放在⼀个服务器上, 通过包管理器可以很⽅便的获取到这个编译好的软件包, 直接进⾏安装.

软件包和软件包管理器, 就好⽐ "App" 和 "应⽤商店" 这样的关系.

yum(Yellow dog Updater, Modified)是Linux下⾮常常⽤的⼀种包管理器. 主要应⽤在Fedora,

RedHat, Centos等发⾏版上.

Ubuntu:主要使⽤apt(Advanced Package Tool)作为其包管理器。apt同样提供了⾃动解决依

赖关系、下载和安装软件包的功能。

1-2 Linux软件⽣态

        Linux下载软件的过程(Ubuntu、Centos、other).

        

        我们的云服务器在下载东西的时候的过程就是你通过你的yum/apt输入你要下载的软件包的名字,它就会去一个软件包的服务器中去查找,如果找到了就返回下载,没有找到就会报出问题。

        操作系统的好坏评估--- ⽣态问题

        一个操作系统的好坏评估就是通过这六个问题来看的,如果你的社区论坛讨论的人多,更多的问题可以被发现并且有更多的人提供解决方案,官网文档齐全,软件体系好,维护速度快,操作系统自身稳定性高,客户群体多,那你就是一个好的操作系统。

为什么会有⼈免费特定社区提供软件,还发布?还提供云服务器让你下载?
        开发者通过发布源代码编译发布各种的软件包,上线到软件包服务器中,本质的目的就是赚钱。
软件包依赖的问题
        这个就是我们的apt和yum的作用,如果我们自己下载的话,我们还需要把软件的各种依赖也得整理着下载一下,太麻烦时间成本太高了,所以设计出了apt和yum来帮助我们解决一下这个的依赖关系,他会自动下载我们需要的依赖和一些库。
        
        我们现在国内的大多软件包服务器都是直接把国外的cp一下,弄了一个国内的链接去使用的。

        

        不仅有软件包服务器中的软件源,还有一些开发者写的弄了一个扩展的软件源,我们的操作系统也是可以下载使用的,代码如下。

        sudo yum install -y epel-release

        

1-3 yum具体操作

1-3-1 查看软件包

        通过 yum list 命令可以罗列出当前⼀共有哪些软件包. 由于包的数⽬可能⾮常之多, 这⾥我们需要使⽤grep 命令只筛选出我们关注的包.
        

1-3-2 安装软件

通过 yum, 我们可以通过很简单的⼀条命令完成 gcc 的安装.
        
        我这里已经下载过了。

yum/apt 会⾃动找到都有哪些软件包需要下载, 这时候敲 "y" 确认安装.

出现 "complete" 字样或者中间未出现报错, 说明安装完成.

注意事项:

安装软件时由于需要向系统⽬录中写⼊内容, ⼀般需要 sudo 或者切到 root 账⼾下才能完成.

yum/apt安装软件只能⼀个装完了再装另⼀个. 正在yum/apt安装⼀个软件的过程中, 如果再尝试⽤

yum/apt安装另外⼀个软件, yum/apt会报错.

如果 yum / apt报错, 请⾃⾏百度.(因为可能出现的问太多了,大家可以用一下我们国内的大模型让它帮你解决一下)

1-3-3 卸载软件

        仍然是⼀条命令

        

        这就是删除我们的安装包的。

        

1-3-4 注意事项

关于 yum / apt 的所有操作必须保证主机(虚拟机)⽹络畅通!!!
可以通过 ping 指令验证
        ping你可以理解为你上网的意思,举个例子。
        
        这里就是测试你上百度的一个网络情况。
        

二.  编辑器Vim

        

        下面直接看这个的使用吧。

        

2-1 vim的基本概念

课堂上我们讲解vim的三种模式(其实有好多模式,⽬前掌握这3种即可),分别是命令模式(commandmode)、插⼊模式(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

我这⾥⼀共有12种模式:six BASIC modes和six ADDITIONAL modes.

        

2-2 vim的基本操作

        

进⼊vim,在系统提⽰符号输⼊vim及⽂件名称后,就进⼊vim全屏幕编辑画⾯:

$ vim test.c

不过有⼀点要特别注意,就是你进⼊vim之后,是处于[正常模式],你要切换到[插⼊模式]才能够

输⼊⽂字。

[正常模式]切换⾄[插⼊模式]

输⼊a

输⼊i

输⼊o

[插⼊模式]切换⾄[正常模式]

⽬前处于[插⼊模式],就只能⼀直输⼊⽂字,如果发现输错了字,想⽤光标键往回移动,将该字删

除,可以先按⼀下「ESC」键转到[正常模式]再删除⽂字。当然,也可以直接删除。

[正常模式]切换⾄[末⾏模式]

「shift + ;」, 其实就是输⼊「:」

退出vim及保存⽂件,在[正常模式]下,按⼀下「:」冒号键进⼊「Last line mode」,例如:

: w (保存当前⽂件)

: wq (输⼊「wq」,存盘并退出vim)

: q! (输⼊q!,不存盘强制退出vim)

        上面的你都可以了解着看一下,我们下面也来演示一下。

        

        

        此时我们进入到vim也就是记事本中,默认是命令模式,此时你无法输入,如果你想退出shift+;=:q,此时就可以退出命令模式了,如果你想输入数据,按下i就进入插入模式了

        此时你可以任意的输入一些数据,如果你想退出的话,先按Esc进入命令模式,然后还是Shift+;wq表示保存退出,我们的Shift+;进入的就是我们的底行模式,我们的插入模式进入底行模式中间必须经过我们的命令模式,无法直接进入。

        

        这个表示的是你进入的时候光标指向的是第一行的意思,如果不加默认就是你上次使用停留的行数。

        我们按两下y就是复制了我们这一行的内容。按下p就可以粘贴出来了,我们输入n+p就会粘贴指定的n次,n+yy也可以复制我们当前行之后的n行。

        我们按下gg就会到第一行去,我们按下Shift+g就会进入到最后一行,我们按下n+Shift+g就会进入到我们的指定行数。

        我们按下我们的hjkl就会有对应的走向,看下图就很清楚了。

        

        这是我们的老键盘,它就是通过我们的hjkl来控制上下左右的。u是撤销我们刚才指令实现的操作的,Ctrl+r就是撤销我们u实现的操作的,就是回复原样的。

        还有上面我们说的一些快捷键,大家也可以试一下。

        进入命令模式之后我们按住Shift+~它会自动进行大小写切换。

        

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操作总结

三种模式
正常模式
插⼊模式
底⾏模式
我们⼀共有12种总模式,⼤家下来可以研究⼀下
vim操作
打开,关闭,查看,查询,插⼊,删除,替换,撤销,复制等等操作
        这些都是重要的指令,上面的操作你们可以试着用一下。
        我们来看一下这些操作。
        我们这个文件中有一千多行的东西,试一下第一个操作。
        
        按一下x删除一个,前面加上n就是删除光标及其之后的n个内容。
        
我输入的是5rw就表示我把光标及其后面的一共n个数都换成w。
  看一下这个批量注释是怎么完成的。
   我们就是按下ctrl+v进入
   
  进入到这个可视化模块,然后找到你要注释的那一行或者多行,如果是多行的话,你找到第一行要注释的,然后看看有多少行直接使用这个n+Shift+g这个命令跳到全部的注释行,然后按下Shift+i健进入到我们的插入模式下,然后就直接写入//然后回车就可以完成多行注释了,我们想注释1-5行,我们来看一下。
        通过gg找到了第一行。
        
        
        点击5+Shift+g找到第五行,然后Shift+i。
        这里只显示第一行输入,你先输入进去。、
        
        按下Esc即可。
        
        前五行被注释了,那么怎么解除注释呢?
        
        还是ctrl+v进入这个可视化模块,然后通过移动光标或者输入n+Shift的方式,此时只选中了第一个字符,所以你可以让它向右移动一下光标,选中注释,然后直接d就删除了
        这样我们的注释操作就完成了。
        我们下面再来演示一下底行进行的代码操作。
        
        就是这个命令,就是把我们光标指向的哪一行的sb换成了hr,我们想把全部的sb换成hr怎么操作呢?
        就是这样进行操作的。
        我们也可以在底行输入命令的。
        
        
        要加一个!表示要强制执行的,或者如果一个文件没有给你w权限,你也可以通过!+i的形式也能进入到我们的插入模式,
        它在外面执行了这个代码。
        我们要是vim一个不存在的文件,此时它就会在这个文件夹中创建这个文件。
        
        我们vim了一个不存在的文件。
        
        此时我们发现文件夹中自动创建了它,下面我们再来看看分屏操作,就是同时打开多个文件。
        
        我们首先打开了我们的code.cpp,然后我还想打开我们的code.c还有其他的,怎么办呢?
        
        输入这个指令。
        
        此时就完成了分屏操作了,光标在哪就操作哪个文件,然后我们按ctrl+ww此时就可以切换到另外一个文件当中了。
        

2-7 简单vim配置[了解]

配置⽂件的位置
在⽬录 /etc/ 下⾯,有个名为vimrc的⽂件,这是系统中公共的vim配置⽂件,对所有⽤⼾都有
效。
⽽在每个⽤⼾的主⽬录下,都可以⾃⼰建⽴私有的配置⽂件,命名为:“.vimrc”。例如,/root
⽬录下,通常已经存在⼀个.vimrc⽂件,如果不存在,则创建之。
切换⽤⼾成为⾃⼰执⾏ su ,进⼊⾃⼰的主⼯作⽬录,执⾏ cd ~
打开⾃⼰⽬录下的.vimrc⽂件,执⾏ vim .vimrc
        我们写代码应该有语法提醒什么的,但是这里确是什么都没有,我们应该怎么操作呢?
        我们只需要按照上面的要求创建一个.vimrc文件,写入内容即可。
        下面我来操作一下。
        
        先通过cd ~进入到你的家目录中,然后通过我们的家目录进入到etc/vim中,然后
        
        创建我们的私有文件。
        
        我们此时创建了这个文件然后写入我们的一些使用的选项。
        你可以通过大模型搜索,然后找到你需要的选项,这里我们只加一个最简单的演示一下。
        加入行号的一个操作。
        
        
        此时默认就有行号了,这个你可以怎么理解呢?
        就是执行vim命令时,他会先找到我们的.vimrc这个文件,先把里面的选项实行一下,然后再进入我们的要编写的文件当中。

三. 编译器gcc/g++

        3-1 背景知识

1. 预处理(进⾏宏替换/去注释/条件编译/头⽂件展开等)

2. 编译(⽣成汇编)

3. 汇编(⽣成机器可识别代码)

4. 链接(⽣成可执⾏⽂件或库⽂件)

3-2 gcc编译选项

        

3-2-1 预处理(进⾏宏替换)

预处理功能主要包括宏定义,⽂件包含,条件编译,去注释等。

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

实例: gcc –E hello.c –o hello.i

选项“-E”,该选项的作⽤是让 gcc 在预处理结束后停⽌编译过程。

选项“-o”是指⽬标⽂件,“.i”⽂件为已经过预处理的C原始程序。

        我们下面来执行一下这个程序。
        
        我们写了这样的一个c语言的程序。
        
        我们可以看到这个code.e文件有八百多行,上面的
        这些内容都是进行的头文件展开宏替换什么的操作,此时我们上面的代码中可以看出,他把我们的注释内容去掉了,只存在我们需要打印的东西。
        
        这几行代码的意思。
        
        这个预处理之后,它只打印我们需要打印的东西,去掉无用的。
        
        头文件展开,就是把头文件中相关的内容,直接拷贝到我们的源文件,预处理完毕,其实就可以不用头文件了!

3-2-2 编译(⽣成汇编)

在这个阶段中,gcc ⾸先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的⼯作,

在检查⽆误后,gcc 把代码翻译成汇编语⾔。

⽤⼾可以使⽤“-S”选项来进⾏查看,该选项只进⾏编译⽽不进⾏汇编,⽣成汇编代码。

实例: gcc –S hello.i –o hello.s

        我们下面还用刚刚的例子。
        这是我们生成的一个code.s文件,它就是我们之前c++学的汇编语言。

3-2-3 汇编(⽣成机器可识别代码)

汇编阶段是把编译阶段⽣成的“.s”⽂件转成⽬标⽂件
读者在此可使⽤选项“-c”就可看到汇编代码已转化为“.o”的⼆进制⽬标代码了
实例: gcc –c hello.s –o hello.o
        
        此时生成了一些乱码,我们看虽然是乱码,但是机器去识别就是一些二进制的数字,汇编这一步的作用就是把我们的汇编文件变成我们机器认识的二进制的文件。
        这个文件也叫做可重定位目标二进制文件,它是不可执行的。
        
        它是可重定位的文件没说是可以执行的文件。
        
        

3-2-4 连接(⽣成可执⾏⽂件或库⽂件)

在成功编译之后,就进⼊了链接阶段。
实例: gcc hello.o –o hello

        

        此时我们形成的code就是可执行文件了,上图中我们能发现它是可以执行的。

                这里就显示它是一个可执行的文件。

         这一步的作用就是和库中连接一下,是我们写的方法什么的都有对应的依赖,地址什么的,使它可以运行。

        

        我们最开始的时候,是通过纸带打孔的方式来编程的,后面出现的汇编语言,拥有了汇编语言我们的编译器也就随之诞生了,它通过把你写的代码转为汇编进而转为二进制文件。

        

3-3 动态链接和静态链接

        

在我们的实际开发中,不可能将所有代码放在⼀个源⽂件中,所以会出现多个源⽂件,⽽且多个源⽂ 件之间不是独⽴的,⽽会存在多种依赖关系,如⼀个源⽂件可能要调⽤另⼀个源⽂件中定义的函数, 但是每个源⽂件都是独⽴编译的,即每个*.c⽂件会形成⼀个*.o⽂件,为了满⾜前⾯说的依赖关系,则 需要将这些源⽂件产⽣的⽬标⽂件进⾏链接,从⽽形成⼀个可以执⾏的程序。这个链接的过程就是静态链接。静态链接的缺点很明显:

浪费空间:因为每个可执⾏程序中对所有需要的⽬标⽂件都要有⼀份副本,所以如果多个程序对

同⼀个⽬标⽂件都有依赖,如多个程序中都调⽤了printf()函数,则这多个程序中都含有printf.o,所以同⼀个⽬标⽂件都在内存存在多个副本;

更新⽐较困难:因为每当库函数的代码修改了,这个时候就需要重新进⾏编译链接形成可执⾏程

序。但是静态链接的优点就是,在可执⾏程序中已经具备了所有执⾏程序所需要的任何东西,在

执⾏的时候运⾏速度快。

动态链接的出现解决了静态链接中提到问题。动态链接的基本思想是把程序按照模块拆分成各个相对 独⽴部分,在程序运⾏时才将它们链接在⼀起形成⼀个完整的程序,⽽不是像静态链接⼀样把所有程序模块都链接成⼀个单独的可执⾏⽂件。

        

       在我们linux下, .so结尾的是动态库,.a结尾的是静态库,我们发现我们默认生成的可执行文件都是动态的。

在这⾥涉及到⼀个重要的概念: 库

我们的C程序中,并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,⽽没有定义函数的实现,那么,是在哪⾥实“printf”函数的呢?

最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库⽂件中去了,在没有特别指定时,gcc 会到系统默认的搜索路径“/usr/lib”下进⾏查找,也就是链接到 libc.so.6 库函数中去,这样就能实现函数“printf”了,⽽这也就是链接的作⽤。

        我们的这个库文件的看法就是去掉前面的lib去掉全部后缀就是我们的库名字了。我们的libc.so.6实际就是我们的c语言库。

        我们要是想实现静态连接该怎么办呢?

        

        我们只需要在后面加上-static就可以了。

        此时我们也可以发现,它比我们动态连接形成的可执行程序所占的磁盘空间大的多。

        

3-4 静态库和动态库

静态库是指编译链接时,把库⽂件的代码全部加⼊到可执⾏⽂件中,因此⽣成的⽂件⽐较⼤,但在运

⾏时也就不再需要库⽂件了。其后缀名⼀般为“.a”

动态库与之相反,在编译链接时并没有把库⽂件的代码加⼊到可执⾏⽂件中,⽽是在程序执⾏时由

运⾏时链接⽂件加载库,这样可以节省系统的开销。动态库⼀般后缀名为“.so”,如前⾯所述的

libc.so.6 就是动态库。gcc 在编译时默认使⽤动态库。完成了链接之后,gcc 就可以⽣成可执⾏⽂

件,如下所⽰。 gcc hello.o –o hello

gcc默认⽣成的⼆进制程序,是动态链接的,这点可以通过 file 命令验证。

        怎么理解我们的静态库和动态库呢?

        

        就是比如你考上了一个重点高中,但是你有一个习惯就是去上网,然后你就问了一下你的大哥,看看附近是否存在网吧,然后你就经常偷偷的出去上网,此时我们的这个网吧就叫作我们的动态库,你就是一个可执行程序,依赖于外部的库中的依赖或者什么的才能使用这个方法,那个库就叫做动态库,也叫共享库,因为其他人也可以使用,有一天老师发现了,叫来了帽子叔叔,然后把网吧端了,此时你就无法玩电脑了,但是你的学习成绩很优异,你爸爸给校长给你申请了一个权益,就是你可以带电脑去上学,此时你爸爸给你买了一个电脑,此时这个电脑只属于你自己,这个电脑就相当于静态库,上面也有它的优缺点。动态库的优点就是节约资源。

3-5 gcc其他常⽤选项 - 了解即可

-E 只激活预处理,这个不⽣成⽂件,你需要把它重定向到⼀个输出⽂件⾥⾯

-S 编译到汇编语⾔不进⾏汇编和链接

-c 编译到⽬标代码

-o ⽂件输出到 ⽂件

-static 此选项对⽣成的⽂件采⽤静态链接

-g ⽣成调试信息。GNU 调试器可利⽤该信息。

-shared 此选项将尽量使⽤动态库,所以⽣成⽂件⽐较⼩,但是需要系统由动态库.

-O0

-O1

-O2

-O3 编译器的优化选项的4个级别,-O0表⽰没有优化,-O1为缺省值,-O3优化级别最⾼

-w 不⽣成任何警告信息。

-Wall ⽣成所有警告信息。

        

最后做个总结。

3-6 总结

        

四. ⾃动化构建-make/Makefile

        

4-1 背景

会不会写makefile,从⼀个侧⾯说明了⼀个⼈是否具备完成⼤型⼯程的能⼒

⼀个⼯程中的源⽂件不计数,其按类型、功能、模块分别放在若⼲个⽬录中,makefile定义了⼀

系列的规则来指定,哪些⽂件需要先编译,哪些⽂件需要后编译,哪些⽂件需要重新编译,甚⾄

于进⾏更复杂的功能操作

makefile带来的好处就是⸺“⾃动化编译”,⼀旦写好,只需要⼀个make命令,整个⼯程完全

⾃动编译,极⼤的提⾼了软件开发的效率。

make是⼀个命令⼯具,是⼀个解释makefile中指令的命令⼯具,⼀般来说,⼤多数的IDE都有这

个命令,⽐如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可⻅,makefile

都成为了⼀种在⼯程⽅⾯的编译⽅法。

make是⼀条命令,makefile是⼀个⽂件,两个搭配使⽤,完成项⽬⾃动化构建。

4-2 make和makefile的使用

        makefile的首字母可以大写也可以小写。

        我们多的不说,直接使用一下。

        

        我们需要先创建一个makefile文件。

        然后vim进入到这个文件。

            这两行表示我们想用code.c形成一个code文件,然后第二行要以TAB健开头然后写下你要怎样形成我们的code文件,然后保存退出,我们直接make一下,此时它就会自己在我们的这个文件夹中寻找我们的makefile文件,执行。

            

        此时就形成了可执行文件。

        下面再来表演一个用法

        我们写了一个clean函数,作用就是删除我们的可执行文件。

        

        此时我们make一下这个方法,自动就执行了。

        依赖关系+依赖方法=才能达到我们的最终目的。

        下面我们来理解一下这两个名词。

        比如我们月底要生活费的时候,你给你爸打电话说,爸我是你儿子,这就表明了依赖关系,你得给我打钱,这就是依赖方法,此时你就达到了你的最终目的。

        这个.PHONY叫做我们的伪目标,我们的make执行程序的时候,默认执行的是makefile中的第一个目标,也就是我们的gcc生成的code可执行文件,如果我们把clean放在前面,它就默认执行clean了。

        伪目标的特点:伪目标总是被执行的。

        我们要理解总是被执行就要先理解不被执行,

        我们发现我们make的时候。我们的这个gcc编译只让我们编译一遍,不让我们编译第二遍,这是为什么呢?

        因为如果我们makefile中有很多个文件的话,你要是修改了几个文件,需要重新make一下,此时你的全部都要重新编译,编译效率变低了,所以不被重复编译就是为了提高编译效率。

        但是我们如果加上.PHONY的话,此时就可以一直编译了。

        

        此时可以一直执行。

        还有一个问题,它怎么知道我需不需要更新呢?

        

        我们每个文件都存在这几个时间,其中一个是修改时间,它就是通过这个来看的。

        Access是访问时间,Modify是修改内容时间,Change是修改属性变化时间,但是我们往往修改内容的时候,文件的大小会发生改变,所以这个change时间就会随着Modify改变那么我们每次修改内容或者属性,你都访问这个文件了 啊,为什么Access这个访问时间不变呢?

        因为新内核改变了标准,访问时间参考意义不大,但是改变次数太多了,增加了磁盘的工作量,所以新内核修改了标准,访问随机次,只改变一次。

        

        它就是通过这个时间轴,我们生成的src.exe可执行时间的mod时间肯定比src.c的mod的时间新,如果出现的相反的情况那么就会重新编译了。

        其实这个过程并不是如图那么简单的,真正的过程如下图所示,只不过编译器帮我们做了。

        

        

        这是我们的经常的写法,让它在生成一个.o文件方便我们。

        

        它会回显我们的命令。

        

        我们加个@一下,关闭回显。

        

        此时不回显命令了。

        

        这就相当于给我们的命令起了个别名。

        为什么要定义变量呢?

        可以写出通用的代码,方便随时替换,类似我们的C的宏和全局变量。

        

        我们的$@代表的是我们上面冒号左边的内容也就是$(BIN),我们的$^表示的是冒号右边的所有内容。

        我们刚才也说了,经常是需要生成.o文件的,我们可以再给两个变量。

        

        如图所示。

        但是还存在一个问题,就是每次源文件什么的都是写死的。

        

        我们可以这样修改一下,这个wildcard *.c这个函数的意思就是拿到我们这个文件夹中所有以.从、后缀结尾的文件,OBJ这个就是把所有的.c文件给我们替换成.o文件,我们把结果打印一下看看。

        

        大概就是这个样子,我们创建了100个file文件,发现都得到了替换,并且都打印了出来。

        我们还说了,需要它生成我们的.o文件呢,这该怎么实现呢?不能一个一个的写吧。

        

        我们可以这样实现,我们的%就表示所有的,%o:%c就表示对应的%c我想转成对应的%o,我们的$<表示的就是我们的所有的.c文件,后面不写系统默认生成的是该文件名的.o文件,如果你有100个.c文件,这个语句就执行100次类似于调用函数是的,这样就完成了调用,生成了我们的.o文件。

        

        把每个.c文件都给它一遍。

        

        看到没,这个$<就拿到了我们的.c文件对应的变量都得到了替换(此时我们的文件夹中只有一个code.c的文件)。

        

        这就是我们最后稍加完善最终的代码了。

        

五. Linux第⼀个系统程序−进度条

        5-1 补充 - 回⻋与换⾏

        回车和换行是一个概念吗

        回车是\r他表示的是把光标移动到我们的下一行的开始,但是换行\n表示的是回车+换行两个概念。

        

        我们写了这样的一个代码,运行一下发现它会先打印出来我们的hello wrold然后再停留两秒。

         

        我们把换行符去掉了,我们发现它是先停留两秒然后才打印,但是代码都是从上往下运行的,这是怎么回事呢?

        就是因为我们的打印语句的内容在缓冲区中。

        缓冲区是非常麻烦的,这里不细讲了。简单说一下就是我们的程序结果输出到显示器上的策略就是行刷新,我们不加换行符的话,我们的这两个语句就是在同一行当中的,所以全部执行完成才会刷新显示器,显示内容。

        程序退出结束时刷新。

        如果我们想直接刷新呢?

               

        此时我们只需要加上如上图所示中的函数,那么它就会直接刷新显示屏,先显示出来我们的打印信息了。

        下面一个问题。

        

        我们看上图的例子,我们打印到显示屏上的a是整数还是字符串呢?

        我们直接说结论,是字符串。

        从4个字符变成了六个字符,我们scanf输入的时候实际上输入的也是字符串。

        

        我们写了这样一个代码,我们正常运行一下。

        

        发现是没有问题的。

        但是如果我们换成\r的话,我们来看一下。

        

        我们发现停留了九秒就自动退出了,我们的东西并没有打印出来,这是为什么呢?

        

        

        意思就是\r每次拉到行首,导致我们的内容被覆盖了,最后的1被我们打印的命令行覆盖了,你可以这样理解。所以此时就需要我们使用强制打印了,就是那个函数,我们上面用过,大家可以自己试试。

      

        我们换成10看一下。

          

        他为什么会是10,90,80...10 这样的呢?因为我们这里默认的是回到行首,每次只覆盖一个字符,这也侧面证明了我们的整数在显示器上显示的时候实际上显示的就是字符了,如果是整数那么不可能只覆盖一个数,一个数字就是一个整体,肯定全部覆盖了,所以是字符。我们该怎么修改呢?

        

        这样就可以了,表示每次覆盖两个字符,你可以这样理解。

        

        再写一个-这样就表示左对齐了。

        

5-2 进度条的实现

       我们是通过三个文件来实现的。

        

        

        我们是这样实现的。

        这个-100的作用就是我们不是一百个字符吗,然后就是预留一百个位置,然后就可以保持右边位置的]保持不动,我们的-号就是让它左对齐,要不然就会从后往前输出了。

        %%和\\都是转义符的意思,两个表示一个的意思。

        \r就是让它保持在同一行的。

        大家可以自己模仿着写一下看看。

        

六. 版本控制器Git

        

        不知道你⼯作或学习时,有没有遇到这样的情况:我们在编写各种⽂档时,为了防⽌⽂档丢失,更改失误,失误后能恢复到原来的版本,不得不复制出⼀个副本,⽐如:

“报告-v1”

“报告-v2”

“报告-v3”

“报告-确定版”

“报告-最终版”

“报告-究极进化版”

...

每个版本有各⾃的内容,但最终会只有⼀份报告需要被我们使⽤ 。

        但在此之前的⼯作都需要这些不同版本的报告,于是每次都是复制粘贴副本,产出的⽂件就越来越 多,⽂件多不是问题,问题是:随着版本数量的不断增多,你还记得这些版本各⾃都是修改了什么吗?

⽂档如此,我们写的项⽬代码,也是存在这个问题的!!

 

6-1 版本控制器

为了能够更⽅便我们管理这些不同版本的⽂件,便有了版本控制器。所谓的版本控制器,就是能让你了解到⼀个⽂件的历史,以及它的发展过程的系统。通俗的讲就是⼀个可以记录⼯程的每⼀次改动和版本迭代的⼀个管理系统,同时也⽅便多⼈协同作业。

⽬前最主流的版本控制器就是 Git 。Git 可以控制电脑上所有格式的⽂件,例如 doc、excel、dwg、dgn、rvt等等。对于我们开发⼈员来说,Git 最重要的就是可以帮助我们管理软件开发项⽬中的源代码⽂件!

       版本控制器就是我们每次修改和删除代码的时候会有上次代码的备份。

         

6-2 git 简史

同⽣活中的许多伟⼤事物⼀样,Git 诞⽣于⼀个极富纷争⼤举创新的年代。

Linux 内核开源项⽬有着为数众多的参与者。 绝⼤多数的 Linux 内核维护⼯作都花在了提交补丁和保 存归档的繁琐事务上(1991−2002年间)。 到 2002 年,整个项⽬组开始启⽤⼀个专有的分布式版本 控制系统 BitKeeper 来管理和维护代码。

到了 2005 年,开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束,他们收回了 Linux 内核社区免费使⽤ BitKeeper 的权⼒。 这就迫使 Linux 开源社区(特别是 Linux 的缔造者 Linus Torvalds)基于使⽤ BitKeeper 时的经验教训,开发出⾃⼰的版本系统。 他们对新的系统制订了若⼲

⽬标:

速度

简单的设计

对⾮线性开发模式的强⼒⽀持(允许成千上万个并⾏开发的分⽀)

完全分布式

有能⼒⾼效管理类似 Linux 内核⼀样的超⼤规模项⽬(速度和数据量)

⾃诞⽣于 2005 年以来,Git ⽇臻成熟完善,在⾼度易⽤的同时,仍然保留着初期设定的⽬标。 它的速 度⻜快,极其适合管理⼤项⽬,有着令⼈难以置信的⾮线性分⽀管理系统。

        

        我们可以自己创建一个仓库,然后通过点击这个。

找到我们的链接,然后通过git clone 你的链接地址,这样就可以完成把我们的远程的git仓库的内容克隆到我们的linux操作系统的本地文件当中。

        

        我们又在这个文件夹中写了这样子的一个代码,然后

        先初始化一下仓库,然后通过我们的git add命令先把我们写的代码添加到暂存区,然后通过我们下面的代码完成提交操作。

        

        然后通过这个登录一下你的git仓库账号,然后通过

        这个命令推送我们的代码。

        然后通过我们的git push也就把我们本地linux仓库中的代码就提交到我们的远端仓库gitee中了。

        这就是我们上面的步骤。下面我们再来理解一下这些本地仓库远程仓库怎么理解。

        把仓库看成是一个目录,然后里面有一个暂存区文件index,git add操作会把代码先放到暂存区中,当你确定要提交到仓库的时候,通过git commit -m的操作,提交到了本地仓库中了。

        下面我们来看一种情形。

        

        我们把我们新建仓库中的内容克隆到我们windows的一个文件夹中。

        然后在我们的linux中我们也把它克隆过来,并且我们又增加了两个文件,此时我们的linux的这个仓库中的多了两个文件,再把它推送到远程仓库也就是我们的gitee中,然后我们再在windows中写一些内容,看看能不能推送过去。

                

        我们写了一个test.cpp的文件,我们看看能不能传过去。

        

        我们push到远端仓库的时候发现出错了,这是怎么回事呢?

        这是因为我们的远端仓库和我们的这个本地仓库的内容并不一样,也就是我们远端仓库中有的,你本地仓库必须有,但是你本地有的我远端不一定要有,此时远端多了一个moudle1和moudle2,所以此时没有同步到本地仓库,无法推送上去。

        我们必须pull一下。

        

        此时moudle1和moudle2就过来了。

        此时你就可以推送了。

        此时你把windows中的test.cpp推送到了这里,此时你的linux和远端就不同步了,也必须同步一下才能提交。

        只需要记得,如果多人协同开发的时候,别人只要动过仓库,此时你就要同步才能提交代码。

        这个.gitignore是干什么用的?

        

        它会把我们提交代码的时候,会把这些后缀结尾的文件全都过滤掉。

        当前工作区和本地git仓库的关系?

        

        就是我们删除修改文件-对文件本身操作,或者增删改对内容的操作,你进行这些操作都会体现在我们的工作区中,你的git add和git commit这一系列的操作并不是增加把文件弄到我们的暂存区或者是哪里的,而是把相关操作的命令或者操作代码存记录下来的。

        比如你在哪个文件中添加删除了代码,它就会记录add  第几行 增加了什么,还有del 第一行,删除了第一行,都是以这些命令的形式记录下来的,而不是把我们的文件拷贝一份放在这里的。

        这些记录你可以把它理解为都放到一个记事本中,这就是我们个人的本地仓库,我们本地仓库中的.git文件就是存放这些操作的。

        上面想告诉大家的就是,我们的这些仓库为什么能保存我们之前版本的文件,就是保存了我们的修改记录,而不是我们之前版本的副本。

        

七. 调试器 - gdb/cgdb使⽤

        我们写了一个code.c文件,就是一个求和的作用,然后我们直接输入调试指令,我们生成了一个mycmd可执行程序。

        

        此时它报错了,告诉我们不能调试,这是为什么呢?

        因为我们生成的可执行程序默认是在Release模式下的,

        所以我们得把我们的可执行程序弄成Debug版本下的才能调式。

        这该怎么办呢?

        

        只需要加一个-g选项即可,此时我们的mycmd2就是一个可调式的可执行程序了。

        

        我们的Debug版本的文件是比release版本的文件要大的。

        我们该怎么证明我们的软件就是release或者debug版本的呢?

        

        可以使用这个命令,只要我们的下面能显示出来debug的信息,此时它就是debug版本的,release什么都不会显示的。

        这里的l就是list的缩写,就是展示我们代码的作用,如果不是从第一行开始展示的,你可以l 0一下即可。

       输入q就可以退出gdb了,但是我们经常使用的调试器叫做cgdb,下面我们来介绍一下。

        

        我们cgdb打开就是这样的。

        

        我们的b+行号就是在第几行打断点的,我们的info b就是查看断点的。

        d就是删除断点,我们的后面跟着的就是我们的断点编号,Num就是我们的断点编号。

         

        断点编号是递增的,即使我们把断点1删除了,下一个断点编号还是会递增成2.。

        我们打断点在多文件当中是这样的。

        

        文件名加上函数名,再加上行数,这样才是正规打断点的方式,我们输入n就是让断点向下走的。

        

        这个就表示删除所有的断点。

        

  

        这些其他的操作符你们都可以自己尝试着试一下,这里就不再一个一个的试了。

        

        c这个指令就是从这个断点跳到下一个断点处没有断点就到结束处。

        finish就是你进入一个函数直接finish就结束了。

        info i就是查看当前正在debug的程序信息。

        disable+断点编号,禁用断点。

        虽然我们的gdb不方便,但是有些东西也是值得我们使用,下面我们来看一下吧。

        

八. watch

        执⾏时监视⼀个表达式(如变量)的值。如果监视的表达式在程序运⾏期间的值发⽣变化,GDB 会暂停程序的执⾏,并通知使⽤者
        这个指令的作用就是我们的代码如果出问题了,我们可能怀疑是某个变量出现了改变导致的,这个变量理论上是不能改变的,此时我们就可以watch+变量名,表示监视这个变量,此时这个变量改变了,就会通知你。
        
如果你有⼀些变量不应该修改,但是你怀疑它修改导致了问题,你可以watch它,如
果变化了,就会通知你.
监视变量的变化

九.set var确定问题原因

        这个我们用一个例子来看一下。

        

        我们本来想把这个flag定义为1的,但是此时我们不小心定义为0了,此时我们返回的结果就是0,但是我们也不能确定到底是哪里的问题,把这个flag改了也不确定答案对不对,此时就可以通过set var flag=1的方式,重新在监视中跑一下代码,看看打印结果是不是我们心中所想的答案就能确定我们出错点了。

        

十.条件断点

        最后还有一个条件判断断点我们来看一下。

        

        第一个就表示你在第几行在i==30的时候打了个断点,此时你在你的上一个断点处c一下直接跳到i==30处。

        第二局表示的是你给2号断点加了个条件断点i==30的时候,此时这两个断点就是同一个断点。

        至此我们的全部内容就讲完了。

        

十一.结束语

        感谢读到这里的每一位朋友!技术之路漫长,每一次代码的调试、每一个知识点的梳理,都因你的驻足而更有意义。如果文章对你有帮助,欢迎点赞收藏,也期待在评论区和你交流更多技术细节~本期的技术分享就到这里啦!感谢你的耐心观看。文中若有疏漏或更好的优化方案,欢迎随时指出,一起在技术的世界里共同进步!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值