送给大家一句话:
永不言弃,就是我的魔法! ——阿斯塔《黑色四叶草》
ଘ(੭ˊ꒳ˋ)੭✧ଘ(੭ˊ꒳ˋ)੭✧ଘ(੭ˊ꒳ˋ)੭✧
ଘ(੭ˊ꒳ˋ)੭✧ଘ(੭ˊ꒳ˋ)੭✧ଘ(੭ˊ꒳ˋ)੭✧
ଘ(੭ˊ꒳ˋ)੭✧ଘ(੭ˊ꒳ˋ)੭✧ଘ(੭ˊ꒳ˋ)੭✧
从零开始认识动静态库
- 1 前言
- 2 动静态库概述
- 3 建立静态库
- 3.1 背景知识
- 3.2 建立静态库
- 3.3 使用静态库
- Thanks♪(・ω・)ノ谢谢阅读!!!
- 下一篇文章见!!!
1 前言
今天我们来学习动静态库。我们之前有没有使用过库呢???
当然了:
strerror strstr strcpy memset...
等函数都要有具体的实现,那这个具体的实现在哪里呢???就是在我们的库中!
2 动静态库概述
学习了这么多的知识,我们有没有使用过库呢?当然了,我们每次编写文件都会加入头文件,来保证我们可以顺利使用:strerror strstr map list vector
等函数与容器。而想要使用这些接口,一定一定又有对应的实现,那么这个实现是我们自己写的吗?当然不是,而是写在库文件中的。
我们编写一个简单的程序:
1 #include<stdio.h>
2 #include<string.h>
3 #include<stdlib.h>
4
5 int main()
6 {
7 printf("I love you!!!\n");
8 const char* str = "I am a joker!!!\n";
9
10 char* copy = (char*)malloc(sizeof(char) * 128);
11 strcpy(copy , str);
12 printf("%s\n",copy);
13 free(copy);
14
15 return 0;
16 }
我们编译一下,然后使用:ldd 文件名
来查看所使用的库:
这就是使用的库文件!
- 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
- 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
直白一点来讲,假如你想玩游戏,静态库就是买一台电脑放在宿舍,动态库就是去网吧与其他人共享。
Linux系统下基本都是使用动态库:
接下来我们来详细说说静态库和动态库。
实际工作中,80%的情况都是使用动态库!!!
3 建立静态库
我们来谈谈如何建立静态库。
3.1 背景知识
首先我们创建几个头文件和对应的函数实现:
我们现在有两套方法。
之前我们学过gcc编译成功的文件会变成.o
二进制文件(可重定位目标文件),.o
文件再经过链接就形成可执行程序。
OK,接下来我们假设一个场景:
今天,老师布置了一项大作业:要求完成一系列方法。你非常顺利的写出来了对应的.h
和.c
文件。这时候你的舍友来问你
舍友:“哥,那个大作业写了没有?”
你回答:“当然了”
舍友随即就客套道:“那啥哥,等哪天请你吃饭,你看看这个…”。
你瞬间就懂了舍友是想要“借鉴”一下大作业:“不行不行,我给你的话,咱俩的代码风格细节就一样了,出事了怎么办…”
你迟钝了一下
你:“也不是没办法,给你打包成.o
文件用吧”。
在这个情景下,你不会把源代码给舍友,而是选择给他发一份二进制文件与头文件手册。
舍友看见了可发愁了,这怎么用啊,舍友连.o
文件是什么都不知道。你说:“不用慌,这个和.c
文件没有区别,正常调用.h
的函数方法就可以”。舍友于是就试了试:
1 #include"mystdio.h"
2 #include"mymath.h"
3 #include <stdio.h>
4 #include<string.h>
5
6
7 int main()
8 {
9 int a = 5 ;
10 int b = 25;
11
12 printf("%d + %d = %d\n",a,b,Sum(a,b));
13
14 myFILE* fp = my_fopen("./myfile.txt" , "w");
15 if(fp == NULL) return 1;
16
17 const char *message = "这就是我的作业\n";
18
19 my_fwrite(fp,message,strlen(message));
20
21 my_fclose(fp);
22
23 return 0;
24 }
舍友看着编写代码时候也没有报错,心里乐开了花,于是迫不及待的进行编译,这一编译可就出事了:
舍友蒙了,为什么提示找不到对应函数,不是提供了对应的文件了吗?这时你来解围:因为编译链接的时候需要把.o
文件也一起进行:
这下就可以了!!!运行也正常!!!
成功了!!
总结:
- 头文件是一个手册 , 提供函数的声明,告诉客户怎么使用
.o
文件提供实现,我们只需要补上一个main,调用头文件提供的方法,然后与.o进行链接,就可以形成可执行文件!!!
3.2 建立静态库
接着上面的情景:
后来 ,老师见你们能力挺强,于是给你和舍友布置了新作业,这个新作业需要数十个头文件。你一看终于到了大展身手的时候,可你的舍友愁坏了!!!像上次那样,你把所有的.h
和.o
都发给了舍友,舍友一不小心就漏掉了一两个,这可麻烦了。于是打包发给舍友,但是你的舍友不会解包。突然,你想到个新办法!建立一个静态库发给舍友!!!
这个静态库如何使用呢?
通过命令 ar -rc libmyc.a *.o
就可以创建一个静态库
于是你就发给了舍友:
然后继续编译链接就可以了:
这样就好了!!!这样使用一个静态库就集合了大量的.o
文件方法!!!
总结:
- 所谓的库文件本质就是把
.o
文件打包 - 静态库提高了开发效率,避免重复的造轮子!
来看一下具体命令:
- 生成静态库[root@localhost linux]# ar -rc libmymath.a add.o sub.o
ar是gnu归档工具,rc表示(replace and create)
- 查看静态库中的目录列表[root@localhost linux]# ar -tv libmymath.a
rw-r–r-- 0/0 1240 Sep 15 16:53 2017 add.o
rw-r–r-- 0/0 1240 Sep 15 16:53 2017 sub.o
3.3 使用静态库
那打包好了静态库,应该如何使用静态库呢?
我们搭建一个这样的结构:
这时候,如果其他人也想使用,我们就通过打包这个mylib
发给他们。
想要通过这个使用,就要把这个库安装到系统里!就是把.h
头文件安装到操作系统搜索头文件的路径下,.a
文件也是这样
也就是这样,现在我们来试试:
因为现在已经在系统默认路径下了,所以既可以使用< >
而不是" "
1 #include<mystdio.h>
2 #include<mymath.h>
3 #include <stdio.h>
4 #include<string.h>
5
6
7 int main()
8 {
9 int a = 5 ;
10 int b = 25;
11
12 printf("%d + %d = %d\n",a,b,Sum(a,b));
13
14 myFILE* fp = my_fopen("./myfile.txt" , "w");
15 if(fp == NULL) return 1;
16
17 const char *message = "这就是我的作业\n";
18
19 my_fwrite(fp,message,strlen(message));
20
21 my_fclose(fp);
22
23 return 0;
24 }
我们编译链接一下:
唉嗨,怎么找不到呢???
因为之前我们使用的库都是C/C++的库,我们的编译器是认识他们的。而我们写的是第三方库,编译器就不认识。所以我们来认识一个新命令:gcc 文件名 -l库名称
需要注意的是库的名称,我们创建的是libmyc.a
,那么这里写入的库名称应该是myc
,要去掉lib .a
这样就可以了!但是我们十分不建议这样做,不要随意改动操作系统的文件。这样很挫!!!
那可不可以不更改操作系统的文件,还想要在当前目录下使用我们的库呢?
我们在系统文件中删除我们的库之后,编译肯定是要报错的因为:
而此时我们库文件是在mylib
中的
所以gcc就为我们提供了一些选项:
- -I(大写 i) :可以帮助程序员动态的加入头文件的搜索路径
- -L :可以帮助程序员动态加入需要链接的库文件的搜索路径
- -l(小写 L ):指明需要链接的库
来实践一下:
这样就可以不改变系统文件就完成与静态库的链接!!!
其中-I(大写i)
这个选项也可以不使用,但是前提是在代码中包含的头文件就要指明路径:
#inlcude"../mylib/inlclude/mystdio.h"
#inlcude"../mylib/inlclude/mymath.h"
注意一定是使用" "
,因为< >
只会会在系统默认路径下搜索。验证一下:
我们在回忆一下,我们之前学习gcc
的时候说过:
- -static 此选项对生成的文件采用静态链接
- shared 此选项将尽量使用动态库,所以生成文件比较小,但是需要系统由动态库
我们在这里并没有指明-static
,怎么就可以使用了呢?因为当前我们的代码里没有动态库,所以编译器就只可以使用静态库了。
- 编译器默认优先动态链接,没有动态库才会使用静态链接