qscool 2020-01-30
昨天学习了c语言的库函数、静态链接和动态链接相关知识
文章部分参考了以下两个公众号的文章。
https://mp.weixin.qq.com/s/bxWyji5xFBoOnRz_MW3t0g
https://mp.weixin.qq.com/s/OfeKT1KQ5ZzWaqYN6A82gA
1、库函数,他的由来是为了增加在函数的复用性,省去重复劳动而设计出来的,都是高手们交流总结的成功,一开始都是源代码,后来变成了库文件,(.a文件),而且随着使用领域的不断扩大,从glibc,ulibc等等分别实现了很多版本的库函数,他们出为了适应不同平台、适应不同的效率、适应不同的编译大小而设计被设计出来的,很多功能有相似的地方,也有精简的地方。
2、静态链接,最早出现就是静态链接,也就是(.a文件和.h文件),很多商业公司开发的gui或rtos等,如果不提供源代码的情况下,取而代之是提供静态库函数的给用户的,一来保密,而来编译后的文件稳定,不容易出错。
比如我自己通常把stm32的hal文件编译成lib文件,他就是静态库函数,因为这样可以减少编译的时间,arm compiler 5的编译速度好很慢的,而且ARM已经不更新了。但是由于arm编译器的优化比较好,整个lib文件并没有拼装到最好的hex文件中,编译器只会把需要用到的hal文件编译进来,不然,以stm32的flash空间,肯定是容纳不了的。
这里其实使用了两个概念,生成lib,类似于生变静态链接库,最后使用静态链接的方式,把程序连接到stm32中,因为,stm32的flash里面不像linux等大型系统一样集成有标准库函数,他是裸机的。所以这个意义上讲,stm32收静态链接。把所需要的程序都装进flash中,包括ARM公司自己的microLIB库(如果勾选的话)。其实这里还涉及到inline函数,后续再讲。
3、动态链接,这个在linux、Windows系统中是比较常见的,静态链接和动态链接生成的可执行文件大小相差很大的,通常gcc等编译器是动态链接的,因为操作系统中都集成了标准函数库,如果需要把自己的代码编译成库函数的形式,那么gcc编译的时候就需要带关键字了,否则会提示找不到函数的错误,这个可以参考gcc的文档。
动态链接的好处是可以减少空间占用。
原文如下:
(4)动态链接库比静态链接库出现的晚一些,效率更高一些,是改进型的。现在我们一般都是使用动态库。静态库在用户链接自己的可执行程序时就已经把调用的库中的函数的代码段链接进最终可执行程序中了,这样好处是可以执行,坏处是太占地方了。尤其是有多个应用程序都使用了这个库函数时,实际上在多个应用程序最后生成的可执行程序中都各自有一份这个库函数的代码段。当这些应用程序同时在内存中运行时,实际上在内存中有多个这个库函数的代码段,这完全重复了。而动态链接库本身不将库函数的代码段链接入可执行程序,只是做个标记。然后当应用程序在内存中执行时,运行时环境发现它调用了一个动态库中的库函数时,会去加载这个动态库到内存中,然后以后不管有多少个应用程序去调用这个库中的函数都会跳转到第一次加载的地方去执行(不会重复加载)。
下面举个简单例子的来分析:
#include <stdio.h>
int main()
{
printf("hello\n");
return 0;
}
接着我们来看一下这个程序编译后占多大内存:
:/home/ubuntu# ls -l
总用量 70756
-rw-r--r-- 1 root root 173 1月 23 02:04 1
-rwxr-xr-x 1 root root 8304 1月 28 21:56 a.out
我们可以看到gcc编译生成的a.out占用8304个字节大小。我们使用静态链接库看看是什么
情况(gcc中编译链接程序默认是使用动态库的):
:/home/ubuntu# gcc hello.c -static
:/home/ubuntu# ls -l
总用量 71576
-rw-r--r-- 1 root root 173 1月 23 02:04 1
-rw-r--r-- 1 root root 75 1月 28 21:58 ‘1\‘
-rwxr-xr-x 1 root root 844704 1月 28 22:02 a.out
我们可以看到使用静态链接库后,它的内存大小就变成了844704个字节了。从中我们可以发现使用静态链接库生成的可执行的程序比较占用内存大小。
所以通常我们下载的vs,gcc,mingw,不仅仅有编译器,而且还有c 语言库函数的头文件等其他文件。