Linux基础(二)

本文详细比较了静态库与动态库在内存使用、加载速度和制作过程上的差异,介绍了静态库的制作步骤和使用静态库和头文件加载的技巧,同时讨论了动态库的制作、链接错误及其解决方法,包括数据段合并和地址回填的概念。
摘要由CSDN通过智能技术生成

一、动态库与静态库的对比

  • 静态库在文件中静态展开,所以有多少文件就展开多少次,非常吃内存,100M展开100次,就是10G,但是这样的好处就是静态加载的速度快
  • 使用动态库会将动态库加载到内存,10个文件也只需要加载一次,然后这些文件用到库的时候临时去加载,速度慢一些,但是很省内存

 二、静态库制作及使用

静态库名字以lib开头,以.a结尾,如:libmylib.a

静态库制作及使用步骤:

1. 将 .c 生成 .o 文件
	gcc -c add.c -o add.o
2. 使用 ar 工具制作静态库
	ar rcs  lib库名.a  add.o sub.o div.o
3. 编译静态库到可执行文件中:
	gcc test.c lib库名.a -o a.out

1.分别构建add.c、sub.c、div1.c如下

//add.c
int add(int a,int b)
{
    return a+b;
}

//sub.c
int sub(int a,int b)
{
    return a-b;
}

//div1.c
int div1(int a,int b)
{
    return a/b;
}

将.c文件生成.o文件,分别执行以下代码:

gcc -c add.c -o add.o
gcc -c sub.c -o sub.o
gcc -c div1.c -o div1.o

2. 使用 ar 工具制作静态库

ar rcs  libmymath.a  add.o sub.o div.o

查看静态库文件属性:file libmymath.a

3. 编译静态库到可执行文件中

新建test.c文件如下:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>

int main(int argc, char *argv[])
{
    int a=14,b = 7;
    printf("%d+%d=%d\n",a,b,add(a,b));
    printf("%d-%d=%d\n",a,b,sub(a,b));
    printf("%d/%d=%d\n",a,b,div1(a,b));

    return 0;
}

直接对test.c进行编译(gcc -c test.c -o test)则出错(链接时),原因:未加入静态库导致未声明。

解决:使用静态库进行编译

gcc -c test.c  libmymath.a -o test

再在test.c中加入乘法函数,以及在main函数中打印:

int mul(int a,int b)
{
    return a*b;
}

通过命令:gcc -c test.c  libmymath.a -o test进行编译,再运行,也能得出乘法结果 

但是在编译时加入参数:gcc -c test.c  libmymath.a -o test -Wall,则出现警告信息

通过编译器隐式声明来解决,即在test.c中加入隐式声明。编译器只能隐式声明返回值为int的函数形式:int add(int ,int );

如果函数不是返回的int,则隐式声明失效,会报错。在test.c中则加入如下隐式声明:

int add(int,int);
int sub(int,int);
int div1(int,int);

重新进行编译:gcc -c test.c  libmymath.a -o test -Wall,警告信息便不存在

以上方法需知道库里的函数,再加到代码里,较为麻烦。

下面使用头文件的方法来加载静态库:

新建mymath.h如下:

#ifndef _MYMATH_H_
#define _MYMATH_H_

int add(int ,int);
int sub(int ,int);
int div1(int ,int);

#endif

以上代码为头文件守卫,防止头文件重复包含

test.c改为:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>

#include "mymath.h"

int mul(int a,int b)
{
    return a*b;
}

int main(int argc, char *argv[])
{
    int a=14,b = 7;

    printf("%d+%d=%d\n",a,b,add(a,b));
    printf("%d-%d=%d\n",a,b,sub(a,b));
    printf("%d/%d=%d\n",a,b,div1(a,b));
    printf("%d x %d = %d\n",a,b,mul(a,b));
    return 0;
}

再进行编译:gcc -c test.c  libmymath.a -o test -Wall,同样得出结果,此方法更加科学

再者,将mymath.h放入inc目录中,libmymath.a放入lib目录下,通过以下命令进行编译:

gcc test.c ./lib/libmymath.a -o test -I ./inc

三、动态库制作及使用

动态库制作及使用步骤:

1. 生成位置无关的.o文件
    gcc -c add.c -o add.o -fPIC
    使用这个参数过后,生成的函数就和位置无关,挂上@plt标识,等待动态绑定
2. 使用 gcc -shared 制作动态库
	gcc -shared -o lib库名.so add.o sub.o div.o
3. 编译可执行程序时指定所使用的动态库. -l:指定库名 -L:指定库路径
	gcc test.c -o a.out -l mymath -L ./lib
4. 运行可执行程序./a.out

1.对add.c、sub.c、div1.c生成位置无关的.o文件,分别执行以下代码:

gcc -c add.c -o add.o -fPIC
gcc -c sub.c -o sub.o -fPIC
gcc -c div1.c -o div1.o -fPIC

2.制作动态库 gcc -shared -o lib库名.so add.o sub.o div.o 

gcc -shared -o libmymath.so add.o sub.o div.o

3.编译程序

动态库在lib目录下,头文件在inc目录下,代码如下:

gcc test.c -o a.out -l mymath -L ./lib -I ./inc

4.执行文件./a.out,出错

四、动态库加载错误原因及解决方式

error while loading shared libraries: libxxx.so: cannot open shared object file: No such file or directory

出错原因分析:

链接器: 工作于链接阶段,完成数据段合并和地址回填,工作时需要 -l 和 -L

动态链接器: 加载动态库,工作于程序运行阶段,工作时需要提供动态库所在目录位置(通过环境变量指定动态库所在位置:export LD_LIBRARY_PATH=动态库路径)

执行如下程序:

export LD_LIBRARY_PATH = ./lib

再执行./a.out则成功(法一)(临时生效,终端重启环境变量失效)

当关闭终端,再次执行a.out时,又报错。

这是因为,环境变量是进程的概念,关闭终端之后再打开,是两个进程,环境变量发生了变化。

要想永久生效(法二),需要修改bash的配置文件:.bashrc   建议使用绝对路径

(1)vi ~./bashrc

(2)写入export LD_LIBRARY_PATH = ./lib 保存

(3)..bashrc或者source.bashrc或者重启终端让修改后的.bashrc生效

(4)./a.out成功

配置文件法(法三):

1)sudo vi /etc/ld.so.conf
2) 写入 动态库绝对路径  保存    在库的路径下执行pwd,再复制
3)sudo ldconfig -v  使配置文件生效。
4)./a.out 成功!!!--- 使用 ldd  a.out 查看

五、扩展了解:数据段合并和地址回填

1.在链接阶段,来自不同目标文件的数据段会被合并到一起,形成最终可执行文件中的统一的数据段。数据段:用于存储初始化的全局变量和静态变量的内存区域

2.在编译阶段,编译器通常不知道变量或函数最终在内存中的地址,因此会生成一些带有占位符的代码。链接器在解析和合并不同目标文件中的符号时,会确定这些符号的最终地址

  • 47
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值