
库是已经编写好的、成熟的、可复用的代码。在实际开发中,每个程序通常依赖大量的基础底层库,不可能每次都从零开始编写代码,因此库的存在具有重要意义。
从本质上讲,库是一种可执行代码的二进制形式,能够被操作系统加载到内存中执行。库可分为两种类型:
静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中,程序运行的时候将不再需要静态库。
动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
静态库的优点以及缺点:
动态库的优点以及缺点:
ubuntu动静态库:
ls -l /lib/x86_64-linux-gnu/libc-2.31.so # 动态库
ls -l /lib/x86_64-linux-gnu/libc.a # 静态库

制作静态库时,准备mystring.h、 mystring.c、mystdio.h、mystdio.c和main.c文件。
main.c
#include "mystdio.h" ""代表是自定义头文件
#include "mystring.h"
#include <string.h>
#include <unistd.h>
int main()
{
MyFile *filep = MyFopen("./log.txt", "a");
if(!filep)
{
printf("fopen error!\n");
return 1;
}
int cnt = 10;
while(cnt--)
{
char *msg = (char*)"hello myfile!!!";
MyFwrite(filep, msg, strlen(msg));
MyFFlush(filep);
printf("buffer: %s\n", filep->outbuffer);
sleep(1);
}
MyFclose(filep);
const char *str = "hello bit!\n";
printf("strlen: %d\n",my_strlen(str));
return 0;
}
mystring.h和mystring.c
// mystring.h
#pragma once
int my_strlen(const char *s);
// mystring.c
#include "mystring.h"
int my_strlen(const char *s)
{
const char *end = s;
while(*end != '\0') end++;
return end - s;
}
mystdio.h
#pragma once
#define MAX 1024
#define NONE_FLUSH (1<<0)
#define LINE_FLUSH (1<<1)
#define FULL_FLUSH (1<<2)
typedef struct IO_FILE
{
int fileno; //文件描述符
int flag;
char outbuffer[MAX]; //语言层面缓冲区
int bufferlen; //缓冲区长度
int flush_method; //刷新模式
}MyFile;
MyFile* MyFopen(const char* path, const char* mode);
void MyFclose(MyFile *);
int MyFwrite(MyFile *, void* str, int len);
void MyFFlush(MyFile *);
mystdio.c
#include "mystdio.h"
#include <sys/types.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
static MyFile* BuyFile(int fd, int flag)
{
MyFile* f = (MyFile*)malloc(sizeof(MyFile));
if(f == NULL) return NULL;
f->bufferlen = 0;
f->fileno = fd;
f->flag = flag;
f->flush_method = LINE_FLUSH;
memset(f->outbuffer, 0, sizeof(f->outbuffer));
return f;
}
MyFile* MyFopen(const char* path, const char* mode)
{
int fd = -1;
int flag = 0;
if(strcmp(mode, "w") == 0)
{
flag = O_CREAT | O_WRONLY | O_TRUNC;
fd = open(path, flag, 0666);
}
else if(strcmp(mode, "a") == 0)
{
flag = O_CREAT | O_WRONLY | O_APPEND;
fd = open(path, flag, 0666);
}
else if(strcmp(mode, "r") == 0)
{
flag = flag = O_RDONLY;
fd = open(path, flag);
}
if(fd < 0) return NULL;
return BuyFile(fd, flag);
}
void MyFclose(MyFile* file)
{
if(file->fileno < 0) return;
//关闭文件时将缓冲区刷新
MyFFlush(file);
//系统调用关闭缓冲区
close(file->fileno);
free(file);
}
int MyFwrite(MyFile* file, void* str, int len)
{
memcpy(file->outbuffer + file->bufferlen, str, len);
file->bufferlen += len;
if((file->flush_method & LINE_FLUSH) && file->outbuffer[file->bufferlen - 1] == '\n')
{
MyFFlush(file);
}
return 0;
}
void MyFFlush(MyFile* file)
{
if(file->bufferlen <= 0) return;
//系统调用写入到内核缓冲区,让操作系统自行刷新
int n = write(file->fileno, file->outbuffer, file->bufferlen);
(void)n;
//系统调用刷新内核缓冲区
fsync(file->fileno);
file->bufferlen = 0;
}
使用gcc命令编译以上文件形成同名的.o文件:
gcc -c mystdio.c
gcc -c mystring.c
![![[Pasted image 20250316201912.png]]](https://i-blog.csdnimg.cn/direct/02bf455a7dfe4b94bc955b05f4c4c47d.png)
接着再使用归档命令ar来将这些.o文件打包形成库文件,其中-r选项表示将目标文件替换到库文件中,-c选项表示库文件不存在时,创建一个新的库文件。
ar -rc libmyc.a *.o
![![[Pasted image 20250316202739.png]]](https://i-blog.csdnimg.cn/direct/b2ea41c62b2b4429956e4eed58ed8bc3.png)
值得注意的是,库的命名是有要求的,需要加上前缀lib和后缀.a,所以我们上面形成的库文件的名字是myc,使用该库时直接引用的名字也应该去掉前缀和后缀。
接着我们使用gcc 命令将main.c编译链接形成可执行程序,但是需要加上-L选项来指明库文件的路径,-l选项指明链接库的名字。
gcc -o main main.c -L. -lmyc # 在当前路径寻找myc静态库
![![[Pasted image 20250316204902.png]]](https://i-blog.csdnimg.cn/direct/b70b6b2465984f8cb85640b91d55c961.png)
上面的示例是将静态库安装在当前目录下的,接着我们将静态库安装在指定目录。
执行以下指令,在当前目录下创建一个存放库文件和头文件的新目录:
mkdir -p stdc/include
mkdir -p stdc/lib
cp -f *.h stdc/include
cp -f *.a stdc/lib
![![[Pasted image 20250316211347.png]]](https://i-blog.csdnimg.cn/direct/0809dde1490541f0bde9db279c56e31c.png)
然后我们再使用gcc进行编译,-I选项指定头文件的搜索路径,-L选项指定库文件的搜索路径,-l选项指定链接具体的哪个库。
gcc -o main main.c -I ./stdc/include/ -L ./stdc/lib/ -lmyc
![![[Pasted image 20250316211347.png]]](https://i-blog.csdnimg.cn/direct/a57434766896484ab337dfb9a6edc17e.png)
除了上面的两种方法,还可以将我们的库安装在系统的目录下,这样上面的gcc编译指令的使用就可以简化:
sudo cp *.h /usr/include/ # 将自定义头文件拷贝到系统目录
sudo cp libmyd.a /lib64/ # 将自定义库拷贝到系统目录
将自定义库安装在系统目录下的做法极其不推荐,这样会将系统的库污染,而且使用的时候还是需要加上-l选项指定链接库的名字(我们的库是一个第三方的库)。
制作动态库依然使用之前的文件作为示例。
使用gcc命令将.c编译生成.o文件时需要加上-fPIC选项,-fPIC选项时位置无关码(position independent code):
gcc -fPIC -c mystdio.c
gcc -fPIC -c mystring.c
将.o文件打包形成动态库不需要别的命令,使用gcc即可,需要加-shared选项。
gcc -o libmyc.so *.o -shared
![![[Pasted image 20250316214958.png]]](https://i-blog.csdnimg.cn/direct/031ca7dc0165463babade7edbcb65d94.png)
使用file命令可以查看文件的属性,可以看到我们制作的库确实是动态库(shared object)
在当前目录下使用动态库:
gcc -o main main.c -L. -lmyc
![![[Pasted image 20250316220608.png]]](https://i-blog.csdnimg.cn/direct/2de0e2ec73fa4a4a96b448653e9833ef.png)
但是执行main时却发现以下报错,原因在于动态库的链接只有在程序执行起来时才会真正的去链接,系统是不知道自定义动态库系统的路径,而静态库没有这样的问题是因为在编译时已经将静态库拷贝到可执行文件里。
![![[Pasted image 20250316220646.png]]
![[Pasted image 20250316221842.png]]](https://i-blog.csdnimg.cn/direct/20414a6a124f415f9b589874559bba8f.png)
在Linux中,有以下的方法来使用第三方动态库:
sudo cp libmyc.so /lib/
这个方法依然不推荐,因为会污染系统库。
/lib/的软连接sudo ln -s /home/Evan/LinuxCode/linux/libso/libmyc.so /lib/libmyc.so
LD_LIBRARY_PATH环境变量LD_LIBRARY_PATH是程序运行时动态查找库时所要搜索的路径,只需将动态库所在的目录路径添加到LD_LIBRARY_PATH环境变量中,系统就知道自定义动态库所在路径。
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/Evan/LinuxCode/linux/libso
