赞
踩
首先我们得知道:
gcc 默认编译是动态链接;
gcc使用静态链接编译需要加参数static
;
准备源文件:mytest.c
使用gcc 默认编译后,得到 mytest
可执行文件:
使用 file 命令
查看可执行程序mytest
:发现使用的是动态链接:
使用 ldd
命令可以查看可执行程序 ,mytest
的动态链接库:
假如gcc -static
静态编译 源文件:mytest.c
:那么就是静态编译:
gcc mytest.c -o mytest_static -static # 静态编译 mytest.c为mytest_static
使用 file 命令
查看可执行程序mytest_static
:发现使用的是静态链接:
使用 ldd
命令可以查看可执行程序 ,mytest_static
的动态链接库:发现没有使用动态链接库:
对比:动态链接编译和静态链接编译的可执行程序大小:发现静态链接的比动态链接的大:
原因:静态链接时把库文件加载到和源文件一起编译形成呢可执行文件;所以很大;
而动态链接时,编译源文件为可执行程序,需要用到动态库的哪个函数,就会跳转到该动态库的那个函数去执行,并不会把动态库所有文件和源文件一起编译;
我们的云服务器的静态库和动态库的位置在:
注意默认情况:云服务器没有静态库,只有动态库;没有静态库,也就是无法使用 gcc -static
的选项;
所以我们假如没有静态库:那么安装:sudo yum -install -y glibc-static
;
不管时静态库还是动态库,本质都是二进制文件,该库就无法查看里面有什么函数功能方法的实现;
那么我们如何查看该库有什么函数呢?
那就是通过提供该库的头文件,头文件就是为了暴露该库的接口的,由于头文件时二进制文件,所以我们可以查看到该库有什么函数接口;
对于一个完整的库来说:包括三个文件
为什么C/C++要把声明放在.h
中,实现放在.c
或者.cpp
或者.cc
中呢?
原因1:方便维护;
原因2:就是为了制作库!制作库是想把库给别人用,但是不想给源代码别人用,所以用.h来暴露接口;
静态库的制作:
准备文件:
add.h sub.h add.c sub.c
将add.c
和 sub.c
制作成静态库;/add.h/ #ifndef __ADD_H__ #define __ADD_H__ int add(int a, int b); #endif // __ADD_H__ /add.c/ #include "add.h" int add(int a, int b) { return a + b; } /sub.h/ #ifndef __SUB_H__ #define __SUB_H__ int sub(int a, int b); #endif // __SUB_H__ /add.c/ #include "sub.h" int sub(int a, int b) { return a - b; }
制作静态库:
生成静态库,需要先对源文件进行汇编操作 (使用参数 -c) 得到二进制格式的目标文件 (.o 格式),
然后在通过 ar 工具将目标文件打包就可以得到静态库文件了 (libxxx.a)。
使用ar
工具创建静态库的时候需要三个参数:
参数c
:创建一个库,不管库是否存在,都将创建。
参数s
:创建目标文件索引,这在创建较大的库时能加快时间。
参数r
:在库中插入模块 (替换)。默认新的成员添加在库的结尾处,如果模块名已经在库中存在,则替换同名的模块。
制作做静态库的步骤:
制作静态库的makefile文件:
查看以下当前的目录文件:
当我们make后:就会多出 .o
文件和 静态库:ibmath.a
,这个就是静态库;
然后我们在 make output
就可以发布了静态库:
查看output的内容:就是我们的头文件和静态库;
制作成功,我们就可以make clean 情况不必要的资源了:
然后你的把output
打包就可以直接给别人使用了你的静态库了;
在搞一个源文件出来mypro.c
它要使用静态库:
首先把output文件改名为lib,因为这样更加明确名字:
查看目录结构:
我们开始编译:
gcc mypro.c -o mypro -L./lib -lmath
解释参数:gcc
编译成功:执行结果为:
生成动态链接库是直接使用 gcc 命令并且需要添加 -fPIC(-fpic) 以及 -shared 参数。
-fPIC 或 -fpic 参数的作用是使得 gcc 生成的代码是与位置无关的,也就是使用相对位置。
-shared参数的作用是告诉编译器生成一个动态链接库。
生成动态链接库的具体步骤如下:
-c
, 还需要添加额外参数-fPIC
,.o
文件打包成动态库,还是使用 gcc
, 使用参数 -shared
指定生成动态库 (位置没有要求);制作动态库的makefile文件:
只要我们make
,就可以得到动态库:libmath.so;
再 make output
,就可以得到动态库和头文件的打包文件output;
make clean
清理资源;
再把得到的 output给别人就可以使用了l
当我们试着像使用静态库那样使用动态库:发现编译没错,执行时候没有链接动态库;
原因是:当你编译源文件时候,只是告诉了编译器,库文件和头文件所在的路径而已;
当你执行了可执行程序,这个可执行程序就和加载器有关了,和编译器无关了;
库的工作原理
静态库如何被加载
在程序编译的最后一个阶段也就是链接阶段,提供的静态库会被打包到可执行程序中。当可执行程序被执行,静态库中的代码也会一并被加载到内存中,因此不会出现静态库找不到无法被加载的问题。
动态库如何被加载
在程序编译的最后一个阶段也就是链接阶段:
在 gcc 命令中虽然指定了库路径 (使用参数 -L ), 但是这个路径并没有记录到可执行程序中,只是检查了这个路径下的库文件是否存在。
同样对应的动态库文件也没有被打包到可执行程序中,只是在可执行程序中记录了库的名字。
可执行程序被执行起来之后:
程序执行的时候会先检测需要的动态库是否可以被加载,加载不到就会提示上边的错误信息;
当动态库中的函数在程序中被调用了,这个时候动态库才加载到内存,如果不被调用就不加载;
动态库的检测和内存加载操作都是由动态连接器来完成的;
动态链接器是一个独立于应用程序的进程,属于操作系统,当用户的程序需要加载动态库的时候动态连接器就开始工作了,很显然动态连接器根本就不知道用户通过 gcc 编译程序的时候通过参数 -L 指定的路径;
所以当我们使用动态库时候,需要告知加载器,去哪找到我们的动态库:
在系统中有一个环境变量:LIB_LIBRARY_PATH
,只要我们给该变量赋值:动态库所在的路径即可;
export LD_LIBRARY_PATH=动态库所在的路径
这样就行了;但是每次重新启动一个新的终端,就会失效;这种方式
另一种:永久生效方式:添加配置文件
cd /etc/ld.so.conf.d/
touch myfile.conf #创建一个自己的.conf文件
vim myfile.conf #打开后复制动态链接库的路径进去,wq保存退出即可
ldconfig #更新链接库
上面操作没有权限就用sudo执行
假如要删除该文件直接
rm myfile.conf# 删除动态链接库的文件
ldconfig #更新
上面操作没有权限就用sudo执行
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。