gcc——静态库与动态库应用

gcc——静态库与动态库应用

转载请标明出处!

1. 基础知识

关于 gcc 编译器命令详解,我会专门更新一篇博文去从头到尾讲述它的原理、用法及命令。

那本篇文章主要是针对 gcc 静态库与动态库作一番总结,网上很多 gcc 自定义库制作都不是非常的全面,所以这里我们来详细扒一扒 Linuxgcc 自定义库制作方法。

1.1 shell命令

那简单来提一下本章内容会用到的 gcc 编译器基本shell 命令。

  • -v(–version)
    • gcc 版本
  • -Wall
    • 查看警告信息
  • -c
    • 将源文件编译为目标文件
  • -o
    • 将目标文件链接为可执行文件
  • -I / -i
    • 搜索头文件目录/单个的位置
  • -L / l
    • 搜索库文件目录/单个的位置

1.2 文件

  • lib_funS.a

    • 静态库
  • lib_funS.so

    • 动态库
  • test

    • 可执行文件
gcc -Wall main.c /usr/lib/lib_funS.a -o test

1.3 环境变量

文件是在 .bash_profile 当中

  • C_INCLUDE_PATH
    • C头文件路径
  • CPLUS_INCLUDE_PATH
    • C++头文件路径
  • LIBRARY_PATH
    • 告诉操作系统,要到这个地方查找动态库,链接期间搜索静态库。
  • LD_LIBRARY_PATH
    • 告诉操作系统,要到这个地方查找动态库,运行期间搜索动态库。

路径与路径之间可以加冒号来进行区分。

DIR1:DIR2:DIR3

2. 创建模板

在当前目录下创建四个文件,包含三个源文件和一个头文件,分别为 fun_a.cfun_b.cmain.cfun.h

main.c

#include "fun.h"

int main()
{
    
    
    fun_a();
    fun_b();
    return 0;
}

fun_a.c

#include "fun.h"

void fun_a(void)
{
    
    
    printf("Hello Home!\n");
}

fun_b.c

#include "fun.h"

void fun_b(void)
{
    
    
    printf("Hello girl!\n");
}

fun.h

#ifndef _FUN_H
#define _FUN_H

#include "stdio.h"

void fun_a(void);
void fun_b(void);

#endif

3. 静态库

静态库从创建、链接到运行,可分为三步骤。

3.1 目标文件

将需要生成静态库的所有源文件,生成目标文件(.o)。也就是将 fun_a.cfun_b.c 对应生成 fun_a.ofun_b.o

注意

不包括 main.c 文件。

gcc -Wall -c fun_a.c fun_b.c

3.2 静态库

然后我们把所有的目标文件(.o)结合生成静态库文件,取名叫做 lib_funS.a (后缀 .a 结尾的文件)。

ar cr lib_funS.a *.o

// 等价于(二选一)
ar cr lib_funS.a fun_a.o fun_b.o

注意

  • 最好我们命令为 lib_xxx.a ,后面我们链接生成执行文件将会说到。

  • 我们可以通过以下命令实现查询静态库包含的文件。

ar t lib_funS.a

如图:

3.3 链接生成执行文件

生成了静态库之后,有三种方式可以实现程序的运行,那我们来仔细扒一扒能通过什么样的方式完成呢?

我们就可以通过静态库与 main.c 链接成可执行文件 (test)。

gcc -Wall main.c lib_funS.a -o test

没有任何报错,我们在来执行看下结果。

./test

注意

这里的 main.c 和 lib_funS.a` 不能够调换位置,否则会导致出错。

gcc -Wall lib_funS.a main.c -o test

如图:
在这里插入图片描述

扩展

普通链接

我们除了第一种链接方式,我们还可以采用最为常规的方式链接成可执行文件(test),就是把所有目标文件不通过静态库方式。

先把 main.c 生成 main.o,因为我们在前面没有去生成。

gcc -Wall -c main.c
gcc -Wall *.o -o test

// 等价于(二选一)
gcc -Wall main.o fun_a.o fun_b.o -o test

没有任何报错,我们在来执行看下结果。

./test
缺陷链接

以上两种方法可行,第三种方法有“雷区”,需要大家注意!!!

gcc -Wall main.c -l_funS -o test

结果如图:
在这里插入图片描述

这种方式是有缺陷的,什么缺陷呢?gcc 编译器寻找不到 lib_funS.a 这个静态库,默认 gcc 编译器只会查找默认或指定的目录下是否存在库文件。

再这里,我们说一下,gcc 默认查找头文件区域在:

  • /usr/local/include
  • /usr/include

gcc 默认查找库文件区域在:

  • /usr/local/lib
  • /usr/lib

为了解决这个问题,我们有三种方式可以解决,从而达到 gcc 编译器能够查找到 lib_funS.a文件,且不会报错。

方式一:

所以,我们需要把 lib_funS.a 这个静态库,移动系到其中的一个目录下,编译器才不会报错。

sudo cp lib_funS.a /usr/lib

// 等价于(二选一)
sudo cp lib_funS.a /usr/local/lib

再进行重新链接**(提醒:这里的 l_funS 就是 lib_funS 静态库的缩写。)**

gcc -Wall main.c -l_funS -o test

没有任何报错,我们在来执行看下结果。

./test
方式二:

那我们还可以通过命令 -L 查找当前静态库的位置,相对路径绝对路径两种都可以实现链接。

gcc -Wall main.c -L. -l_funS -o test

//(二选一)
gcc -Wall main.c -L/路径 -lhello -o h3

没有任何报错,我们在来执行看下结果。

./test
方式三:

添加环境变量,LIBRARY_PATH 添加了静态库的绝对路径,也可以实现链接。

export LIBRARY_PATH=/路径:$LIBRARY_PATH

然后我们链接,发现并没有报错了。

gcc -Wall main.c -l_funS -o test

没有任何报错,我们在来执行看下结果。

./test
总结:

我们来说说这三种方式,我们应该采用哪种比较好?首先方式一直接修改系统库路径当中文件,这点一定是不推荐的,这样会导致你的系统库越来越大并且会占据更多的内存空间,而且万一手抖删掉了其它配置文件,可能会导致系统崩溃。方式三,修改环境变量,也是不推荐,因为不利于我们做移植,每个环境下都要重新配置环境变量,可知它的麻烦。。所以我们采用方式二这种方式。

3.4 运行程序

执行 ./可执行文件 ,就可以运行程序了。

3.5 扩展

文件结构

我们之前说的都是所有文件在同一个目录下,那如果是一个工程项目,一定是会有若干个文件夹与文件组成。这里,我们以最基础的文件结构来说明,大家会懂得举一反三。为了方便管理,通常头文件放在 inc 文件夹当中,库文件放在 lib 文件夹当中。
在这里插入图片描述

main.c

#include "hello.h"

int main()
{
    
    
    hello();
    return 0;
}

hello.c

#include "hello.h"

void hello(void)
{
    
    
    printf("Hello World!\n");
}

hello.h

#ifndef _HELLO_H
#define _HELLO_H

#include <stdio.h>

void hello(void);
#endif

过程

将所要生成库文件的源文件都生成目标文件(.o)

gcc -Wall -c -Iinc hello.c

将目标文件链接成静态库

ar cr lib_funS.a hello.o

静态库移动到 lib 文件夹当中。

mv lib_funS.a lib

到这里,我们采用方式二来完成,其它方式大家可以自己尝试。

main.c 编译成目标文件。

gcc -Wall -Iinc -c main.c

在将 main.o 与 静态链接库 生成可执行文件。

gcc -Wall main.o -Llib -l_funS -o test

运行可执行文件。

./test

4. 静态库与动态库区别

4.1 静态库(static libraries)

Linux下静态库以后缀 .a 命名,在windows下以后缀 .lib 命名。

4.2 动态库(shared libraries)

Linux下静态库以后缀 .so 命名,在windows下以后缀 .dll 命名,这里采用方式二 来完成,其它方式请参照以上方式。

gcc -fPIC -shared hello.c -o lib_DLL.so
gcc main.c -L. -l_DLL -o test
./test

4.3 区别

如果使用静态库,由机器码构成,将调用函数拷贝到目标文件当中,个头比较大,占的内存比较多。而动态库不是把机器码拷贝到目标文件,而是拷贝small table(内部是地址),虽增加了磁盘空间,但大大减少对内存的浪费。而且操作系统提供虚拟内存机制,使得如果有多个进程调用一个动态库,只需要留一个动态库拷贝,可以节省极大对内存浪费。

gcc 编译器默认是使用动态库。

猜你喜欢

转载自blog.csdn.net/qq_43125185/article/details/110920098