目录
动静态链接
我们需要了解一个事实:我们所写的c语言的代码,和c语言库是两码事
例如:我们写的printf函数只是调用了printf函数,printf函数的定义在c语言库中,只有在链接的时候,我们的代码和c语言库才产生关联。
链接的本质:我们写的代码和标准库如何进行关联
动态链接
我们对动态链接进行形象化描述:张三在一个没有任何电子设备的学校中,张三想要上网,在学校中是没有办法的,但是张三通过一个学长知道在学校5km外有一个网吧,可以在周末的时候去网吧里上网。
这里的学长相当于就是链接器,也就是你了解网吧(库)的媒介
动态链接有哪些优点?
答:形成的可执行程序小,因为我们的动态链接是相当于我们的代码去库里面找对应函数的定义,所以不会消耗额外的空间。
动态链接有哪些缺点?
答:1:受我们的库的影响,假如我们的库升级了,我们调用的函数对应在库里面的定义就有可能发生改变.
2:时间会相对长一些,因为我们需要在库里面找对应函数的定义。
静态链接:
我们对静态链接进行形象化描述:网吧因为违法经营被取缔了,张三没有办法上网了,但是张三的父亲很开明,给张三买了一台电脑,放在了学校里面,方便张三随时的使用。
静态链接的实质:对于我们写的函数,我们在库中拷贝对应函数的定义,把这些定义放到程序中。
静态链接的特点:
因为我们需要拷贝库到源代码中,所以我们形成的可执行程序就会比较大一些。
只要我们完成了库中的拷贝,我们就不受库升级或者删除的影响。
花费时间较少,因为不需要到库中找函数对应的定义
总结:
动态链接实质:链接时去库中找方法的定义
动态链接的特点:1:链接形成的可执行程序较小。
2:收到库升级或者删除的影响
静态链接的实质:链接时已经把对应的方法拷贝到源代码中,直接进行调用即可
静态链接的特点:1:因为经过了拷贝,形成的可执行程序比较大
2:拷贝之后就不受库的影响了
验证Linux系统下的链接方式
我们首先创建一个普通文件
我们对mytest.c文件进行编辑:
然后我们通过gcc把源文件经过编译链接形成可执行程序,这个可执行程序叫做mytest.c
我们也可以这样写:
这样形成的可执行程序的默认文件名叫做a.out
file +文件名:查看文件类型
我们可以发现,形成的可执行程序有一句这样的话,这里的汉语表示动态链接,所以我们在Linux系统下默认链接时采用的是动态链接。
ldd:查看可执行程序依赖的动态库列表
在Linux下库的命名
我们这里的libc.so.6去掉前缀和后缀,得到的就是c,所以这个lib.so.6对应的就是c标准库
我们也可以在Linux系统下手动执行静态链接:
我们在后面加上-static表示的就是静态链接
我们日常使用的指令在哪里呢?
我们使用的指令都是经过动态链接形成的可执行程序。
我们发现我们的指令也是来自c标准库的。
g++的使用
我们先创建一个文件:
我们通过g++进行编译:
我们使用ldd查看可执行程序以来的动态库列表
我们使用的标准c++库
make和makefile
我们先创建一个普通文件mycode.c
然后使用vim对文件进行编辑:
然后我们创建一个makefile文件:
我们使用vim对文件内容进行编辑
然后我们按make
这里就表示我们把mycode.c链接成为可执行程序mycode
假如我们像执行我们写的hello Makefile文件,我们只需要执行mycode即可
makefile的原理
我们举一个简单的例子:假如你找要父亲要钱,你会怎么说?
我们要表明依赖关系和依赖方法,依赖关系是我是父亲的儿子,这就是依赖关系,依赖方法是给我钱。
在社会上求别人做一件事也是同样如此:首先要表明依赖关系,我是你的朋友,其次要表明依赖方法:怎么做。
这里的依赖关系是我们要形成的mycode是通过mycode.c得到的,左侧是目标文件,右侧是依赖文件列表。
依赖方法是我们通过对源文件mycode.c经过编译和链接形成对应的可执行程序mycode
清理工作:
依赖关系可以为空,表明我们的clean不依赖任何文件夹来形成,依赖方法是删除掉mycode
这里的.PHONY:表明我们修饰的clean是一个伪目标,我们只需要执行依赖方法即可。
如何执行清理
我们输入make clean表示清理
我们的mycode文件被删除了
不需要重复make
之后多次make都不会成功,因为假如我们没有对mycode.c源文件的内容进行修改时,我们就不需要重复make
但是我们的清理工作却可以执行多次
原因在:PHONY:被该关键字修饰的对象是伪目标 ---该目标总是被执行。
假如我们对mycode也进行修饰
接下来,接下来,make 就总是可执行了
make默认执行的是第一个目标
我们要形成的第一个目标是mycode
所以我们使用make默认构建第一个项目
并且make和make mycode是等价的。
makefile的语法:
我们删除掉第一个:PHONY修饰
gcc是如何得知我们已经构建过相同的项目的?
首先,我们引入三个时间的概念:
stat+文件名:显示文件的详细信息
这三个分别表示访问时间 内容修改时间 属性修改时间
例如:
表示减去mycode文件拥有者的写权限,相当于对文件属性进行了修改,所以change时间会发生改变:
只有change发生了改变,证明changge表示的是文件属性被修改的时间
假如我们对文件内容进行修改呢?
假如我们对mycode.c的文件内容进行修改呢?
我们可以发现
无论是文件属性还是文件内容都发生了变化,原因是文件大小也是文件属性的一部分,所以修改文件内容也会导致修改文件属性的时间发生改变。
对于文件的访问时间的理解
对于文件的访问,无论我们是cat来读取文件,还是用vim来编辑文件内容,本质上都形成了文件的访问,为了防止频繁修改文件的访问时间,我们规定在一段时间内,对文件访问的次数到达一定次数时修改文件的访问时间。
所以,讨论文件的访问时间是没有什么意义的
gcc进行链接时,是如何知道我们的文件已经被连接过了?
通过文件的内容修改时间来判断,因为现有源文件才有可执行程序,所以源文件的内容修改时间比较早,我们进行判断,假设源文件的内容修改时间要晚于可执行程序,表示我们对文件内容进行了修改,那么gcc就会对文件进行重新编译链接
.PHONY的意义
.PHONY的意义就是说明不再需要判定文件内容的修改时间来执行文件的编译,而是每次都会被重新执行。
makefile的推导规则
源文件mycode.c经过预处理形成mycode.i,mycode.i经过编译形成mycode.s
mycode.s经过汇编形成mycode.o,mycode.o经过链接形成对应的可执行程序mycode
我们用make执行操作
执行的过程和我们写的过程好像是相反的,原因如下
我们的mycode直接依赖于mycode.o,但是我们并没有mycode.o,所以对应的依赖方法也不执行,以此类推直到最后一行
我们的mycode.i有依赖的对象mycode.c,执行对应的依赖方法,形成mycode.i,再开始由下向上循环,所以我们写的和执行的顺序是相反的。
上面就是makefile的推导规则
进度条:
我们首先要了解休眠指令
接下来,写一个c语言的代码:
然后使用make进行执行操作
然后调用mycode
马上就打印出了youcanseeme
但是当我们去掉youcanseeme 后面的\n时
这时候过了两秒才打印出来you can see me
首先,我们应该先明确一点:我们一定是先调用printf,再调用的sleep
但是注意一点:先调用的printf函数并不代表数据先显示
这里之所以没有显示的原因是数据被放到了缓冲区里面,等到sleep执行完毕之后,数据才被刷新出来。
那么为什么加上\n之后就马上刷新了呢?
有行缓冲,数据被存到了行缓冲,但是\n能够刷新行缓冲。
除了\n,有没有其他的方法能够刷新呢?
我们可以使用函数fflush
例如:
这时候就直接刷新出了数据。
回车换行问题
回车表示的是什么,换行表示的是什么?
答:回车表示的是回到这一行的开头,换行表示换到下一行的相同位置
但是现在的回车包含了这两种意思,既回到了开头,又换了行。
接下来,我们来试图实现倒计时
这显然不是我们想要的倒计时,我们去掉\n,刷新标准输出
这显然也不是我们想要的结果
基本上可以了,但是当我们要倒计时10s时。
原因是我们的10有两个数,1和0,但是我们的\r只替换了前面的1,后面的0一直保留,我们可以修改一下printf,让printf每次打印2位
这时候就可以了。
进度条到下一次我们再讲。
转载:https://blog.csdn.net/qq_66581313/article/details/127664513