文章目录
- 3. 动态库和静态库
- 3.1 静态库与动态库
- 3.2 静态库的制作和使用原理
- 3.3 动态库的制作和使用原理
- 3.3.1 动态库是怎么被加载的
- 3.4 关于地址
3. 动态库和静态库
3.1 静态库与动态库
静态库(
.a
):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库动态库(
.so
):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(
dynamic linking
)动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。
3.2 静态库的制作和使用原理
mymath.h
#pragma once#include <stdio.h>int add(int x, int y);//加
int sub(int x, int y);//减
int mul(int x, int y);//乘
int div(int x, int y);//除
mymath.c
#include "mymath.h"int myerrno = 0;int add(int x, int y){return x + y;
}int sub(int x, int y){return x - y;
}int mul(int x, int y){return x * y;
}int div(int x, int y){if(y == 0){myerrno = 1;return -1;}return x / y;
}
makefile
lib=libmymath.a$(lib):mymath.oar -rc $@ $^
mymath.o:mymath.cgcc -c $^.PHONY:clean
clean:rm -rf *.o *.a lib.PHONY:output
output:mkdir -p lib/includemkdir -p lib/mymathlibcp *.h lib/includecp *.a lib/mymathlib
make后:
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23$ ll
total 28
drwxrwxr-x 2 ydk_108 ydk_108 4096 Jan 24 22:18 ./
drwxrwxr-x 17 ydk_108 ydk_108 4096 Jan 24 22:07 ../
-rw-rw-r-- 1 ydk_108 ydk_108 2024 Jan 24 22:18 libmymath.a
-rw-rw-r-- 1 ydk_108 ydk_108 229 Jan 24 22:18 makefile
-rw-rw-r-- 1 ydk_108 ydk_108 276 Jan 24 22:13 mymath.c
-rw-rw-r-- 1 ydk_108 ydk_108 147 Jan 24 22:12 mymath.h
-rw-rw-r-- 1 ydk_108 ydk_108 1848 Jan 24 22:18 mymath.o
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23$
make output后:
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23$ make output
mkdir -p lib/include
mkdir -p lib/mymathlib
cp *.h lib/include
cp *.a lib/mymathlib
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23$ ll
total 32
drwxrwxr-x 3 ydk_108 ydk_108 4096 Jan 24 22:22 ./
drwxrwxr-x 17 ydk_108 ydk_108 4096 Jan 24 22:07 ../
drwxrwxr-x 4 ydk_108 ydk_108 4096 Jan 24 22:22 lib/
-rw-rw-r-- 1 ydk_108 ydk_108 2024 Jan 24 22:18 libmymath.a
-rw-rw-r-- 1 ydk_108 ydk_108 229 Jan 24 22:18 makefile
-rw-rw-r-- 1 ydk_108 ydk_108 276 Jan 24 22:13 mymath.c
-rw-rw-r-- 1 ydk_108 ydk_108 147 Jan 24 22:12 mymath.h
-rw-rw-r-- 1 ydk_108 ydk_108 1848 Jan 24 22:18 mymath.o
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23$ tree lib
lib
├── include
│ └── mymath.h
└── mymathlib└── libmymath.a2 directories, 2 files
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23$
然后在lesson23
这个目录下创建test
目录,把lib
复制到test
目录下
同时在test
目录下创建main.c
文件
#include "lib/include/mymath.h"int main(){printf("1+1=%d\n",add(1,1));return 0;
}
如果
main.c
文件的头文件写的是#include "mymath.h"
那么直接
gcc
是无法编译的,可以用gcc main.c -I ./lib/include/
当然如果头文件是
#include "lib/include/mymath.h"
就可以直接gcc
了。
gcc
编译后:
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23/test$ gcc main.c
/usr/bin/ld: /tmp/ccErjU5m.o: in function `main':
main.c:(.text+0x13): undefined reference to `add'
collect2: error: ld returned 1 exit status
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23/test$
因为gcc默认去系统的默认库里面找的,所以我们要加点东西:
gcc main.c -L ./lib/mymathlib/ -lmymath
这里的-L
表示的是在lib
库里面,
-lmymath
表示是在./lib/mymathlib/
库里面的libmymath.a
库文件,这里要去掉lib
前缀和.a
后缀。-l
表示库名称。
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23/test$ gcc main.c -L ./lib/mymathlib/ -lmymath
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23/test$ ll
total 36
drwxrwxr-x 3 ydk_108 ydk_108 4096 Jan 24 22:40 ./
drwxrwxr-x 3 ydk_108 ydk_108 4096 Jan 24 22:24 ../
-rwxrwxr-x 1 ydk_108 ydk_108 16872 Jan 24 22:40 a.out*
drwxrwxr-x 4 ydk_108 ydk_108 4096 Jan 24 22:22 lib/
-rw-rw-r-- 1 ydk_108 ydk_108 90 Jan 24 22:26 main.c
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson23/test$
可是gcc main.c -L ./lib/mymathlib/ -lmymath
这个东西太长了,我们可不可以不写这么长的呢?
我们可以把mymath.h
和libmymath.a
放到系统默认路径下,这个操作也叫库的安装。
这样只需要gcc main.c -lmymath
就可以了。
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ll
total 36
drwxrwxr-x 3 ydk_108 ydk_108 4096 Jan 25 10:26 ./
drwxrwxr-x 3 ydk_108 ydk_108 4096 Jan 25 10:27 ../
-rwxrwxr-x 1 ydk_108 ydk_108 16872 Jan 25 10:26 a.out*
drwxrwxr-x 4 ydk_108 ydk_108 4096 Jan 25 10:26 lib/
-rw-rw-r-- 1 ydk_108 ydk_108 90 Jan 25 10:26 main.c
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ sudo cp lib/include/mymath.h /usr/include/
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ls /usr/include/mymath.h
/usr/include/mymath.h
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ sudo cp lib/mymathlib/libmymath.a /lib64/
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ls /lib64/libmymath.a
/lib64/libmymath.a
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ gcc main.c
/usr/bin/ld: /tmp/ccDIK8p6.o: in function `main':
main.c:(.text+0x13): undefined reference to `add'
collect2: error: ld returned 1 exit status
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ gcc main.c -lmymath
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ll
total 36
drwxrwxr-x 3 ydk_108 ydk_108 4096 Jan 25 10:52 ./
drwxrwxr-x 3 ydk_108 ydk_108 4096 Jan 25 10:27 ../
-rwxrwxr-x 1 ydk_108 ydk_108 16872 Jan 25 10:52 a.out*
drwxrwxr-x 4 ydk_108 ydk_108 4096 Jan 25 10:26 lib/
-rw-rw-r-- 1 ydk_108 ydk_108 90 Jan 25 10:26 main.c
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$
不过不建议我们自己乱加文件,这样可能会在以后背刺自己,造成冲突。
注意:如果我们把main.c
写成这样
#include "lib/include/mymath.h"int main(){printf("10/0=%d, error=%d\n",div(10,0),myerrno);return 0;
}
那么运行结果就是
10/0=-1, error=0
为什么会这样呢?这里的运算一看就出错了,为什么error
不是1
呢?
因为C
语言默认是从右向左运算的,这里先把myerrno
的值传进来才传div(10,0)
的值。
我们可以修改一下代码:
#include "lib/include/mymath.h"int main(){int n = div(10,0);printf("10/0=%d, error=%d\n",n,myerrno);return 0;
}
打印:
10/0=-1, error=1
把我们提供的方法,给别人用有两个方法:
我把源文件直接给他
把我们的源代码想办法打包成库 =
库+.h
第三方库,往后使用的时候,必定要是用
gcc-l
深刻理解
errno
的本质如果系统中只提供静态链接,
gcc
则只能对该库进行静态链接如果系统中需要链接多个库,则
gcc
可以链接多个库不带
static
有动态库就动态编译,只有静态库才静态编译。带static
只静态编译。
3.3 动态库的制作和使用原理
makefile
# 定义动态库名称
dy-lib=libmymethod.so
# 定义静态库名称
static-lib=libmymath.a# 声明all为伪目标(不是实际文件)
.PHONY:all
# 默认目标 - 构建动态库和静态库
all: $(dy-lib) $(static-lib)# 从mymath.o创建静态库的规则
$(static-lib):mymath.oar -rc $@ $^ # 创建归档文件,如存在则替换,创建索引
mymath.o:mymath.cgcc -c $^ # 将C文件编译为目标文件# 从mylog.o和myprint.o创建动态库的规则
$(dy-lib):mylog.o myprint.ogcc -shared -o $@ $^ # 将目标文件链接成共享库
mylog.o:mylog.cgcc -fPIC -c $^ # 使用位置无关代码编译
myprint.o:myprint.cgcc -fPIC -c $^ # 使用位置无关代码编译# 清理目标 - 删除所有生成的文件
.PHONY:clean
clean:rm -rf *.o *.a *.so mylib# 输出目标 - 创建发布目录结构
.PHONY:output
output:mkdir -p mylib/include # 创建头文件目录mkdir -p mylib/lib # 创建库文件目录cp *.h mylib/include # 复制头文件cp *.a mylib/lib # 复制静态库cp *.so mylib/lib # 复制动态库
myprint.h
#pragma once#include <stdio.h>void Print();
myprint.c
#include "myprint.h"void Print()
{printf("hello new world!\n");printf("hello new world!\n");printf("hello new world!\n");printf("hello new world!\n");
}
mylog.h
#pragma once#include <stdio.h>void Log(const char*);
mylog.c
#include "mylog.h"void Log(const char*info)
{printf("Warning: %s\n", info);
}
然后编译:make ; make output
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24$ make ; make output
gcc -fPIC -c mylog.c
gcc -fPIC -c myprint.c
gcc -shared -o libmymethod.so mylog.o myprint.o
gcc -c mymath.c
ar -rc libmymath.a mymath.o
mkdir -p mylib/include
mkdir -p mylib/lib
cp *.h mylib/include
cp *.a mylib/lib
cp *.so mylib/lib
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24$ ll
total 76
drwxrwxr-x 4 ydk_108 ydk_108 4096 Jan 25 13:50 ./
drwxrwxr-x 18 ydk_108 ydk_108 4096 Jan 25 10:26 ../
-rw-rw-r-- 1 ydk_108 ydk_108 2024 Jan 25 13:50 libmymath.a
-rwxrwxr-x 1 ydk_108 ydk_108 16312 Jan 25 13:50 libmymethod.so*
-rw-rw-r-- 1 ydk_108 ydk_108 448 Jan 25 13:34 makefile
drwxrwxr-x 4 ydk_108 ydk_108 4096 Jan 25 13:50 mylib/
-rw-rw-r-- 1 ydk_108 ydk_108 85 Jan 25 13:34 mylog.c
-rw-rw-r-- 1 ydk_108 ydk_108 58 Jan 25 13:34 mylog.h
-rw-rw-r-- 1 ydk_108 ydk_108 1704 Jan 25 13:50 mylog.o
-rw-rw-r-- 1 ydk_108 ydk_108 276 Jan 25 10:26 mymath.c
-rw-rw-r-- 1 ydk_108 ydk_108 147 Jan 25 10:26 mymath.h
-rw-rw-r-- 1 ydk_108 ydk_108 1848 Jan 25 13:50 mymath.o
-rw-rw-r-- 1 ydk_108 ydk_108 176 Jan 25 13:35 myprint.c
-rw-rw-r-- 1 ydk_108 ydk_108 49 Jan 25 13:35 myprint.h
-rw-rw-r-- 1 ydk_108 ydk_108 1864 Jan 25 13:50 myprint.o
drwxrwxr-x 3 ydk_108 ydk_108 4096 Jan 25 10:52 test/
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24$ tree mylib
mylib
├── include
│ ├── mylog.h
│ ├── mymath.h
│ └── myprint.h
└── lib├── libmymath.a└── libmymethod.so2 directories, 5 files
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24$
这里的执行过程是:
- 首先执行
make
,因为没有指定目标,所以默认执行 Makefile 中的第一个目标all
,完成所有库文件的编译和创建- 然后执行
make output
,创建目录结构并复制文件这样写的原因是:
- 必须先执行
make
生成所有的库文件(.so
和.a
)- 再执行
make output
进行文件整理和复制- 如果直接执行
make output
,可能会因为库文件还未生成而导致复制失败
然后我们把库复制进test文件夹里面,并且把main.c文件改一下:
#include "mylog.h"
#include "myprint.h"int main()
{Print();Log("hello log function");return 0;
}
运行
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ll
total 40
drwxrwxr-x 4 ydk_108 ydk_108 4096 Jan 25 14:00 ./
drwxrwxr-x 4 ydk_108 ydk_108 4096 Jan 25 13:50 ../
-rwxrwxr-x 1 ydk_108 ydk_108 16872 Jan 25 10:52 a.out* # 之前静态编译留下的
drwxrwxr-x 4 ydk_108 ydk_108 4096 Jan 25 10:26 lib/
-rw-rw-r-- 1 ydk_108 ydk_108 116 Jan 25 13:57 main.c
drwxrwxr-x 4 ydk_108 ydk_108 4096 Jan 25 14:00 mylib/
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ gcc main.c -I mylib/include/ -L mylib/lib -lmymethod
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ll
total 40
drwxrwxr-x 4 ydk_108 ydk_108 4096 Jan 25 14:00 ./
drwxrwxr-x 4 ydk_108 ydk_108 4096 Jan 25 13:50 ../
-rwxrwxr-x 1 ydk_108 ydk_108 16712 Jan 25 14:00 a.out* # 现在动态编译新生成的
drwxrwxr-x 4 ydk_108 ydk_108 4096 Jan 25 10:26 lib/
-rw-rw-r-- 1 ydk_108 ydk_108 116 Jan 25 13:57 main.c
drwxrwxr-x 4 ydk_108 ydk_108 4096 Jan 25 14:00 mylib/
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$
虽然编译过了,但是一运行就这样了:
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ./a.out
./a.out: error while loading shared libraries: libmymethod.so: cannot open shared object file: No such file or directory
为什么呢?
我们之前编译的时候告诉编译器我们的动态库在哪里了,但是我们编译后,并没有告诉系统(即加载器)我们的动态库在哪里。
可以将自己库所在的路径,添加到系统的环境变量
LD_LIBRARY_PATH
中。
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ldd a.outlinux-vdso.so.1 (0x00007ffc9cd79000)libmymethod.so => not foundlibc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fa1947d3000)/lib64/ld-linux-x86-64.so.2 (0x00007fa1949d3000)
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ydk_108/108/lesson24/test/mylib/lib
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ echo $LD_LIBRARY_PATH
::/home/ydk_108/108/lesson24/test/mylib/lib
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ldd a.outlinux-vdso.so.1 (0x00007ffd6835f000)libmymethod.so => /home/ydk_108/108/lesson24/test/mylib/lib/libmymethod.so (0x00007f105f59b000)libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f105f3a2000)/lib64/ld-linux-x86-64.so.2 (0x00007f105f5a7000)
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ./a.out
hello new world!
hello new world!
hello new world!
hello new world!
Warning: hello log function
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ su
Password:
root@iZuf68hz06p6s2809gl3i1Z:/home/ydk_108/108/lesson24/test# cd /etc/ld.so.conf.d
root@iZuf68hz06p6s2809gl3i1Z:/etc/ld.so.conf.d# ll
total 20
drwxr-xr-x 2 root root 4096 Jun 3 2024 ./
drwxr-xr-x 89 root root 4096 Jan 14 19:45 ../
-rw-r--r-- 1 root root 38 Sep 7 2019 fakeroot-x86_64-linux-gnu.conf
-rw-r--r-- 1 root root 44 Apr 15 2020 libc.conf
-rw-r--r-- 1 root root 100 Apr 15 2020 x86_64-linux-gnu.conf
root@iZuf68hz06p6s2809gl3i1Z:/etc/ld.so.conf.d# touch ydk_108.conf
root@iZuf68hz06p6s2809gl3i1Z:/etc/ld.so.conf.d# ll
total 20
drwxr-xr-x 2 root root 4096 Jan 25 14:25 ./
drwxr-xr-x 89 root root 4096 Jan 14 19:45 ../
-rw-r--r-- 1 root root 38 Sep 7 2019 fakeroot-x86_64-linux-gnu.conf
-rw-r--r-- 1 root root 44 Apr 15 2020 libc.conf
-rw-r--r-- 1 root root 100 Apr 15 2020 x86_64-linux-gnu.conf
-rw-r--r-- 1 root root 0 Jan 25 14:25 ydk_108.conf
root@iZuf68hz06p6s2809gl3i1Z:/etc/ld.so.conf.d# pwd
/etc/ld.so.conf.d
root@iZuf68hz06p6s2809gl3i1Z:/etc/ld.so.conf.d# vim ydk_108.conf
root@iZuf68hz06p6s2809gl3i1Z:/etc/ld.so.conf.d# ldconfig
root@iZuf68hz06p6s2809gl3i1Z:/etc/ld.so.conf.d#
# ldconfig前
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ldd a.outlinux-vdso.so.1 (0x00007ffc8e125000)libmymethod.so => not foundlibc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4e24c2d000)/lib64/ld-linux-x86-64.so.2 (0x00007f4e24e2d000)
# ldconfig后
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$ ldd a.outlinux-vdso.so.1 (0x00007fffa231c000)libmymethod.so => /home/ydk_108/108/lesson24/test/mylib/lib/libmymethod.so (0x00007f9b2d7b8000)libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f9b2d5c6000)/lib64/ld-linux-x86-64.so.2 (0x00007f9b2d7cb000)
ydk_108@iZuf68hz06p6s2809gl3i1Z:~/108/lesson24/test$
解决加载找不到动态库的方法:
拷贝到系统默认的库路径
/ib64 /usr/lib64/
在系统默认的库路径
/ib64 /usr/lib64/
下建立软连接将自己的库所在的路径,添加到系统的环境变量
LD_LIBRARY_PATH
中。(重启就消失了)
/etc/ld.so.conf.d
建立自己的动态库路径的配置文件(名字随便写,里面路径写对就行),然后重新ldconfig
即可。(重启不会消失)
实际情况,我们用的库都是别人的成熟的库,都采用直接安装到系统的方式。
3.3.1 动态库是怎么被加载的
动态库(共享库)的共享机制:
- 内存共享机制
- 当第一个进程加载动态库时,动态库的代码段被加载到物理内存中
- 后续进程再使用这个动态库时,不会重新加载代码段,而是直接映射到已加载的物理内存
- 多个进程共享同一份物理内存中的代码段,节省内存资源
- 每个进程有自己的数据段副本,确保数据隔离
动态库在进程运行的时候,是要被加载的(静态库没有)
所以,动态库在系统中加载之后,会被所有进程共享。
这个共享库一旦被多个进程共享,那么它对应的页就会引入计数2,缺页中断的时候,识别发现是被多个进程共享的就会写时拷贝。
3.4 关于地址
程序没有加载前的地址(程序)
程序编译好之后,内部有地址的概念吗?
有的。(可以联想之前C++多态的虚函数表)
库可以在虚拟内存中,任意位置加载。
怎么做到的呢?
让自己内部函数不要采用绝对编址,只表示每个函数在库中的偏移量即可。
例如:printf的地址0x1122,这个0x1122代表printf相对于库的起始地址的偏移量。
然后加载动态库的时候,在地址空间里随便放。只需要记住这个库在虚拟地址空间的起始地址就可以了。
解释:为什么动态库可以被加载到任意位置,并且多个进程可以共享同一份代码?
- 假设有一个动态库 libexample.so:
起始位置: 未知 (加载时才确定)
函数A: +0x100 (偏移量)
函数B: +0x200 (偏移量)
函数C: +0x300 (偏移量)
- 不同进程加载时的情况:
进程1:
- 库加载到虚拟地址 0x10000000
- 函数A实际地址 = 0x10000000 + 0x100 = 0x10000100
- 函数B实际地址 = 0x10000000 + 0x200 = 0x10000200进程2:
- 库加载到虚拟地址 0x20000000
- 函数A实际地址 = 0x20000000 + 0x100 = 0x20000100
- 函数B实际地址 = 0x20000000 + 0x200 = 0x20000200
- 工作原理:
- 编译时:函数地址全部用偏移量表示
- 加载时:
- 动态链接器选择一个可用的虚拟地址空间
- 记录库的基地址
- 所有函数调用时:实际地址 = 基地址 + 偏移量
这就好比:
- 一本书的目录不用页码,而用"距离书开始的页数"
- 无论这本书放在书架的哪个位置(起始地址)
- 只要知道书在哪(基地址),就能通过偏移量找到每个章节
这也就是为什么fPIC
叫做产生位置无关码
。
- 静态库为什么不谈加载呢?
因为静态库会直接把程序拷贝到可执行程序里面,就谈不上加载了。
- 静态库为什么不说与位置无关呢?
因为库方法拷贝到可执行程序里后,就不谈偏移量了。他在什么位置就是确定的了。