成都网站制作价格,app营销策略模板,wordpress菜单高度,定州市住房保障和城乡建设局网站以下内容源于C语言中文网相关内容的学习整理#xff0c;如有侵权请告知删除。
一、库文件的简介 库文件#xff0c;其等价为压缩包文件。该文件内部通常包含不止一个目标文件#xff08;也就是二进制文件#xff09;#xff0c;每个目标文件存储的代码#xff0c;并非完…以下内容源于C语言中文网相关内容的学习整理如有侵权请告知删除。
一、库文件的简介 库文件其等价为压缩包文件。该文件内部通常包含不止一个目标文件也就是二进制文件每个目标文件存储的代码并非完整的程序而是一个个实用的功能模块。例如C 语言库文件提供有大量的函数如 scanf()、printf()、strlen() 等C 库文件不仅提供有使用的函数还有大量事先设计好的类如 string 字符串类。库文件的产生极大的提高了程序员的开发效率。因为很多功能根本不需要从 0 开发直接调取包含该功能的库文件即可。并且库文件的调用方法也很简单以 C 语言中的 printf() 输出函数为例程序中只需引入 stdio.h 头文件即可调用 printf() 函数。 调用库文件为什么还要牵扯到头文件呢首先头文件和库文件并不是一码事它们最大的区别在于头文件只存储变量、函数或者类等这些功能模块的声明部分库文件才负责存储各模块具体的实现部分。即所有的库文件都提供有相应的头文件作为调用它的接口库文件是无法直接使用的只能通过头文件间接调用。头文件和库文件相结合的访问机制最大的好处在于有时候我们只想让别人使用自己实现的功能并不想公开实现功能的源码就可以将其制作为库文件这样用户获取到的是二进制文件而头文件又只包含声明部分这样就实现了“将源码隐藏起来”的目的且不会影响用户使用。 二、静态链接库与动态链接库的简介 C或C程序从源文件到生成可执行文件需经历 4 个阶段分别为预处理、编译、汇编和链接。链接阶段所要完成的工作是将同一项目中各源文件生成的目标文件和程序中用到的库文件整合为一个可执行文件。虽然库文件明确用于链接但编译器提供了2种实现链接的方式分别称为静态链接和动态链接。采用静态链接方式实现链接操作的库文件称为静态链接库采用动态链接方式实现链接操作的库文件称为动态链接库。 在 Linux 发行版系统中静态链接库文件的后缀名通常用 .a 表示动态链接库的后缀名通常用 .so 表示在 Windows 系统中静态链接库文件的后缀名为 .lib动态链接库的后缀名为 .dll。 GCC 编译器生成可执行文件时默认情况下会优先使用动态链接库实现链接操作除非当前系统环境中没有程序文件所需要的动态链接库GCC 编译器才会选择相应的静态链接库。如果两种都没有或者 GCC 编译器未找到则链接失败。 在 Linux 发行版中静态链接库和动态链接库通常存放在 /usr/bin 或者 /bin 目录下。 1、静态链接库 静态链接库实现链接操作的方式很简单即程序文件中哪里用到了库文件中的功能模块GCC 编译器就会将该模板代码直接复制到程序文件的适当位置最终生成可执行文件。 使用静态库文件的优势是可移植性强可独立运行即生成的可执行文件不再需要任何静态库文件的支持就可以独立运行。劣势是代码冗余、可执行文件的体积大如果程序文件中多次调用库中的同一功能模块则该模块代码就会被复制多次生成的可执行文件中会包含多段完全相同的代码造成代码的冗余。和使用动态链接库生成的可执行文件相比静态链接库生成的可执行文件的体积更大。 2、动态链接库 动态链接库又称为共享链接库。采用动态链接库实现链接操作时程序文件中哪里需要库文件的功能模块GCC 编译器不会直接将该功能模块的代码拷贝到文件中而是将功能模块的位置信息记录到文件中直接生成可执行文件。 显然这样生成的可执行文件是无法独立运行的。采用动态链接库生成的可执行文件运行时GCC 编译器会将对应的动态链接库一同加载在内存中由于可执行文件中事先记录了所需功能模块的位置信息所以在现有动态链接库的支持下也可以成功运行。 采用动态链接库的优势是生成的执行文件文件体积小因为可执行文件中记录的是功能模块的地址真正的实现代码会在程序运行时被载入内存即便功能模块被调用多次使用的都是同一份实现代码这也是将动态链接库称为共享链接库的原因。劣势是可移植性差无法独立运行必须借助相应的库文件。 三、静态链接库的创建与使用
1、静态链接库的创建 比如有如下项目。 demo项目├─ headers│ └─ test.h└─ sources├─ add.c├─ sub.c├─ div.c└─ main.c 为了隐藏加法、减法、除法的具体实现可以将add.c、sub.c 以及 div.c 这 3 个源文件加工成静态链接库。并且根据实际需要我们可以将它们集体压缩到一个静态链接库中也可以各自压缩成一个静态链接库。 将源文件打包为静态链接库的过程很简单只需经历以下 2 个步骤 1将所有指定的源文件都编译成相应的目标文件。 [rootbogon demo]# gcc -c sub.c add.c div.c
[rootbogon demo]# ls
add.c add.o div.c div.o main.c sub.c sub.o test.h 2使用 ar 压缩指令将生成的目标文件打包成静态链接库。该指令的基本格式如下。 ar rcs 静态链接库名称 目标文件1 目标文件2 ... 需要注意的是静态链接库不能随意起名需遵循“libxxx.a”这样的命名规则其中xxx 指我们为该库起的名字比如 Linux 系统自带的一些静态链接库名称为 libc.a、libgcc.a、libm.a它们的名称分别为 c、gcc 和 m。 [rootbogon demo]# ar rcs libmymath.a add.o sub.o div.o
[rootbogon demo]# ls
add.c add.o div.c div.o libmymath.a main.c sub.c sub.o test.h 2、静态链接库的使用 静态链接库的使用很简单就是在程序的链接阶段将静态链接库和目标文件一起执行链接操作从而生成可执行文件。 以 demo 项目为例首先我们将 main.c 文件编译为目标文件 [rootbogon demo]# gcc -c main.c
[rootbogon demo]# ls
add.c div.c libmymath.a main.o sub.c
test.h add.o div.o main.c sub.o 在此基础上我们可以直接执行如下命令即可完成链接操作默认生成可执行文件a.out或者利用-o选项生成其他命名文件。-static 选项表示强制 GCC 编译器使用静态链接库。 [rootbogon demo]# gcc -static main.o libmymath.a
[rootbogon demo]# ls
add.c a.out div.o main.c sub.c test.h
add.o div.c libmymath.a main.o sub.o 注意如果 GCC 编译器提示无法找到 libmymath.a因为上面这种写法只会在当前目录查找libmymath.a静态链接库还可以使用如下方式完成链接操作。其中 -L 选项用于向 GCC 编译器指明静态链接库的存储位置 -l小写的 L选项用于指明所需静态链接库的名称注意这里的名称指的是 xxx 部分且建议将 -l 和 xxx 直接连用即 -lxxx中间不需有空格。 [rootbogon demo]# gcc main.o -static -L /root/demo/ -lmymath
[rootbogon demo]# ls
add.c a.out div.o main.c sub.c test.h
add.o div.c libmymath.a main.o sub.o 四、动态链接库的创建与使用
1、动态链接库的创建 使用源文件创建动态链接库其基本格式如下。其中-shared 选项用于生成动态链接库-fpic还可写成 -fPIC选项的功能是令 GCC 编译器生成动态链接库多个目标文件的压缩包时表示各目标文件中函数、类等功能模块的地址使用相对地址而非绝对地址。这样无论将来链接库被加载到内存的什么位置都可以正常使用。另外动态链接库的命令规则和静态链接库完全相同只不过在 Linux 发行版系统中其后缀名用 .so 表示。 gcc -fpic -shared 源文件名... -o 动态链接库名 例如将add.c、sub.c 和 div.c 这 3 个源文件生成一个动态链接库 [rootbogon demo]# ls
add.c div.c main.c sub.c test.h
[rootbogon demo]# gcc -fpic -shared add.c sub.c div.c -o libmymath.so
[rootbogon demo]# ls
add.c div.c libmymath.so main.c sub.c test.h 2、动态链接库的隐式调用 动态链接库的使用场景就是与项目中其它源文件或目标文件一起参与链接生成可执行文件。以 demo 项目为例前面我们将 add.c、sub.c 和 div.c 打包到了 libmymath.so 动态链接库中此时该项目中仅剩 main.c 源程序文件因此执行 demo 项目也就演变成了将 main.c 和 libmymath.so 进行链接进而生成可执行文件。 执行如下指令即意味着隐式调用动态链接库隐式调用意思是将动态链接库和其它源程序文件或者目标文件一起参与链接生成可执行文件main.exe。 [rootbogon demo]# gcc main.c libmymath.so -o main.exe
[rootbogon demo]# ls
add.c div.c libmymath.so main.c main.exe sub.c test.h main.exe 通常无法直接执行因为执行过程中无法找到 libmymath.so 这个动态链接库。 [rootbogon demo]# ./main.exe
./a.out: error while loading shared libraries: libd.so: cannot open shared object file: No such file or directory 通过执行ldd main.exe指令可以查看当前文件在执行时需要用到的所有动态链接库以及各个库文件的存储位置。可以看到main.exe 文件的执行需要 4 个动态链接库的支持其中就包括 libmymath.so但该文件无法找到因此 main.exe 执行会失败。 [rootbogon demo]# ldd main.exe
linux-vdso.so.1 (0x00007fff423ff000)
libmymath.so not found
libc.so.6 /lib64/libc.so.6 (0x00000037e2c00000)
/lib64/ld-linux-x86-64.so.2 (0x00000037e2800000) 运行由动态链接库生成的可执行文件时必须确保程序在运行时可以找到这个动态链接库。常用的解决方案有以下几种。 1将链接库文件移动到标准库目录下例如 /usr/lib、/usr/lib64、/lib、/lib64 2在终端输入export LD_LIBRARY_PATH$LD_LIBRARY_PATH:xxx其中 xxx 为动态链接库文件的绝对存储路径此方式仅在当前终端有效关闭终端后无效 3修改~/.bashrc 或~/.bash_profile 文件即在文件最后一行添加export LD_LIBRARY_PATH$LD_LIBRARY_PATH:xxxxxx 为动态库文件的绝对存储路径。保存之后执行source .bashrc指令此方式仅对当前登陆用户有效。 3、动态链接库的显式调用 显式调用动态链接库的过程类似于使用 malloc() 和 free()C 中使用 new 和 delete管理动态内存空间需要时就申请不需要时就将占用的资源释放。显式调用意思是在编写代码时利用某些函数申请调用库函数以及最后关闭与释放库函数资源。由此可见显式调用动态链接库对内存的使用更加合理。显式调用动态链接库更常应用于一些大型项目中。 和隐式调用动态链接库不同在 C/C 程序中显式调用动态链接库时无需引入和动态链接库相关的头文件。但与此同时程序中需要引入另一个头文件即#include dlfcn.h因为要显式调用动态链接库需要使用该头文件提供的一些函数。 1类似于读写文件前必须先打开文件要想显式调用某个动态链接库提供的资源首先要做的就是打开该库文件。打开库文件其本质就是将库文件装载到内存中为后续使用做准备。打开动态库文件需要借助 dlopen() 函数其语法格式为void *dlopen (const char *filename, int flag) filename 参数用于表明目标库文件的存储位置和库名如果用户提供的是以 / 开头即以绝对路径表示的文件名则函数会前往该路径下查找库文件反之如果用户仅提供文件名则该函数会依次前往 LD_LIBRARY_PATH 环境变量指定的目录、/etc/ld.so.cache 文件中指定的录、/usr/lib、/usr/lib64、/lib、/lib64 等默认搜索路径中查找。flag 参数的值有 2 种。RTLD_NOW将库文件中所有的资源都载入内存。RTLD_LAZY暂时不降库文件中的资源载入内存使用时才载入。2借助 dlsym() 函数可以获得指定函数在内存中的位置其语法格式为void *dlsym(void *handle, char *symbol); hanle 参数表示指向已打开库文件的指针symbol 参数用于指定目标函数的函数名。如果 dlsym() 函数成功找到指定函数会返回一个指向该函数的指针反之如果查找失败函数会返回 NULL。3和 dlopen() 相对地借助 dlclose() 函数可以关闭已打开的动态链接库。该函数的语法格式如下int dlclose (void *handle); 其中handle 表示已打开的库文件指针。当函数返回 0 时表示函数操作成功反之函数执行失败。注意调用 dlclose() 函数并不一定会将目标库彻底释放它只会是目标库的引用计数减 1当引用计数减为 0 时库文件所占用的资源才会被彻底释放。4dlerror() 函数可以获得最近一次 dlopen()、dlsym() 或者 dlclose() 函数操作失败的错误信息。该函数的语法格式如下const char *dlerror(void);可以看到该函数不需要传递任何参数。同时如果函数返回 NULL则表明最近一次操作执行成功。 [rootbogon demo]# cat main.c
#include stdio.h
#include dlfcn.h
int main()
{int m,n;//打开库文件void* handler dlopen(libmymath.so,RTLD_LAZY);if(dlerror() ! NULL){printf(%s,dlerror());}//获取库文件中的 add() 函数int(*add)(int,int)dlsym(handler,add);if(dlerror()!NULL){printf(%s,dlerror());}//获取库文件中的 sub() 函数int(*sub)(int,int)dlsym(handler,sub);if(dlerror()!NULL){printf(%s,dlerror());}//获取库文件中的 div() 函数int(*div)(int,int)dlsym(handler,div);if(dlerror()!NULL){printf(%s,dlerror());}//使用库文件中的函数实现相关功能printf(Input two numbers: );scanf(%d %d, m, n);printf(%d%d%d\n, m, n, add(m, n));printf(%d-%d%d\n, m, n, sub(m, n));printf(%d÷%d%d\n, m, n, div(m, n));//关闭库文件dlclose(handler) return 0;
} 分析main.c 主程序发现其中并没有引入 test.h 头文件因为显式调用动态链接库时不需要它。在使用库文件中相关函数之前先调用 dlopen() 函数打开库文件然后通过 dlsym() 函数找到相关函数最后调用 dlclose() 函数关闭库文件。 编好程序之后通过执行如下指令即可生成相应的可执行文件main.exe。注意这里需要添加 -ldl 选项因为该程序中用到了dlfcn.h头文件对应的动态库文件是libdl.sogcc 命令在编译 main.c 时必须用-ldl指明这个库文件。 [rootbogon demo]# gcc main.c -ldl -o main.exe
[rootbogon demo]# ls
add.c div.c libmymath.so main.c main.exe sub.c test.h 五、GCC找不到库文件的解决方法
1、GCC生成可执行文件时找不到库文件 程序链接阶段指明所用库文件的方式有 2 种。假设当前 mian.c 文件需要借助 libmymath.a 才能完成链接则完成链接操作的 gcc 指令有以下 2 种写法。 [rootbogon demo]# gcc -static main.c libmymath.a -o main.exe
[rootbogon demo]# gcc -static main.c -lmymath -o main.exe 第一种写法完成链接操作时GCC 编译器只会在当前目录中查找 libmymath.a 静态链接库。如果使用第一种方法完成链接操作但 GCC 编译器提示找不到所需库文件表明所用库文件并未存储在当前路径下解决方案就是手动找到库文件并将其移至当前路径然后重新执行链接操作。 第二种写法使用 -l小写的 L选项指明要查找的静态库的文件名则 GCC 编译器会按照如下顺序依次到指定目录中查找所需库文件。 1如果 gcc 指令使用 -L 选项指定了查找路径则 GCC 编译器会优先选择去该路径下查找所需要的库文件 2再到 Linux 系统中 LIBRARY_PATH 环境变量指定的路径中搜索需要的库文件 3最后到 GCC 编译器默认的搜索路径比如/lib、/lib64、/usr/lib、/usr/lib64、/usr/local/lib、/usr/local/lib64 等不同系统环境略有差异中查找。 如果使用的是第二种方法也遇到了 GCC 编译器提示未找到所需库文件表明库文件的存储路径不对解决方案有以下 3 种 1手动找到该库文件并在 gcc 指令中用 -L 选项明确指明其存储路径。比如 libmymath.a 静态库文件存储在 /usr 目录下则完成链接操作的 gcc 指令应为gcc -static main.c -L/usr -lmymath -o main.exe 2将库文件的存储路径添加到 LIBRARY_PATH 环境变量中。仍以库文件存储在 /usr 目录下则通过执行export LIBRARY_PATH$LIBRARY_PATH:/usr指令即可将 /usr 目录添加到该环境变量中此方式仅在当前命令行窗口中有效 3将库文件移动到 GCC 编译器默认的搜索路径中。 2、GCC运行可执行文件时找不到动态库文件 当 GCC 编译器运行可执行文件时会按照如下的路径顺序搜索所需的动态库文件。 1如果在生成可执行文件时用户使用了-Wl,-rpathdir其中 dir 表示要查找的具体路径如果查找路径有多个中间用 : 冒号分隔选项指定动态库的搜索路径则运行该文件时 GCC 会首先到指定的路径中查找所需的库文件 2GCC 编译器会前往 LD_LIBRARY_PATH 环境变量指明的路径中查找所需的动态库文件 3GCC 编译器会前往 /ect/ld.so.conf 文件中指定的搜索路径查找动态库文件 4GCC 编译器会前往默认的搜索路径中例如 /lib、/lib64、/usr/lib、/usr/lib64 等中查找所需的动态库文件。 注意可执行文件的当前存储路径并不在默认的搜索路径范围内因此即便将动态库文件和可执行文件放在同一目录下GCC 编译器也可能提示“找不到动态库”。 因此对于 GCC 运行可执行文件时提示找不到动态库文件的问题常用的解决方法如下。 1将动态库文件的存储路径添加到 LD_LIBRARY_PATH 环境变量中。假设动态库文件存储在 /usr 目录中执行export LD_LIBRARY_PATH$LD_LIBRARY_PATH:/usr指令即可实现此目的此方式仅在当前命令行窗口中有效 2修改动态库文件的存储路径即将其移动至 GCC 编译器默认的搜索路径中。 3修改~/.bashrc 或 ~/.bash_profile 文件即在文件最后一行添加export LD_LIBRARY_PATH$LD_LIBRARY_PATH:xxxxxx 为动态库文件的绝对存储路径。保存之后执行 source .bashrc 指令此方式仅对当前登陆用户有效。 值得一提的是GCC 编译器提供有 ldd 指令借助该指令我们可以明确知道某个可执行文件需要哪些动态库文件做支撑、这些动态库文件是否已经找到、各个动态库文件的具体存储路径等信息。注意如果某个动态库文件未找到则 后面会显示 not found表明 GCC 编译器无法找到该动态库此时该可执行文件将无法执行。 以main.exe 可执行文件为例执行如下 ldd 指令 [rootbogon demo]# ldd main.exe
linux-vdso.so.1 (0x00007fff06fb3000)
libmymath.so /lib64/libmymath.so (0x00007f65b2a62000)
libc.so.6 /lib64/libc.so.6 (0x00000037e2c00000)
/lib64/ld-linux-x86-64.so.2 (0x00000037e2800000)