静态库的制作和使用

        Linux下的静态库为lib*.a格式的二进制文件(目标文件),对应于Windows下的.lib格式的文件。

1)命名规则

        lib+库名字+ .a   libMytest.a ,则库名字为mytest。下面以具体的代码为例介绍如何制作静态库。

//main.c
#include <stdio.h>
#include "head.h" 
int main(void)
{int sum = add(2, 24);printf("sum = %d\n", sum);return 0;
}//head.h
#ifndef __HEAD_H_
#define __HEAD_H_
int add(int , int);      
int sub(int , int);
int mul(int , int);
int div(int , int);
#endif//add.c
int add(int a, int b)
{int result = a + b;return result;
}//div.c
int div(int a, int b)
{int result = a / b;return result;
}//mul.c
int mul(int a, int b)
{int result = a * b;return result;
}//sub.c
int sub(int a, int b)
{int result = a - b;return result;
}

        以上有5段代码:main.c、head.h、add.c、div.c、mul.c和sub.c。要求将add.c、div.c、mul.c和sub.c制作成库文件提供给客服使用,这4个文件都是关于头文件head.h中函数的详细实现,因此为了不想让客服知道函数实现的细节和方法,不能将源码提供给客服,而是以库文件(二进制文件)的形式提供给客服使用即可。至于如何使用,库文件已经将函数接口留在了头文件head.h中,即4个函数声明。用户看了头文件就知道如何使用库文件了,即如何使用函数。因此最后只需要将main.c、head.h和库文件给客服即可。(因此,一般库文件与相对应的头文件是同一个人来完成的)

        先强调一下gcc的一个使用。-c参数是用来生成目标文件.o的,但是不链接。如: gcc -c zsx.s -o zsx.o    gcc -c zsx.c -o zsx.o    对于上面4个.c文件,若工作目录中只有这4个.c文件,可以用*.c表示这四个文件:  gcc -c *.c    则会生成4个.o文件:add.o  div.o  mul.o和sub.o(在不指出输出文件名字时,默认是将.c文件的.c改为.o) 。   gcc -c *.c   等价于对每一个单独的.c文件进行预处理、编译、汇编后生成各自的 .o文件(档案库文件)。同理,对于gcc a.c b.c c.c d.c 最后生成一个a.out文件,其先对每一个源文件生成目标文件,然后将这些目标文件与需要的静态库文件链接形成可执行文件,至于需要的动态库文件则是在程序运行时才会加载进去。      

2)制作静态库

        1)生成对应的.o文件  2)将生成的.o文件打包。 ar rcs + 静态库的名字(libmytest.a)+ 生成的所有的.o  

        一个头文件(head.h,/mnt/hgfs/share/gcc/Calc/include)和四个.c文件(add.c、div.c、mul.c、sub.c, /mnt/hgfs/share/gcc/Calc/src):                                                                                                                                               [root@localhost src]# gcc -c *.c
[root@localhost src]# ls
add.c  add.o  div.c  div.o  mul.c  mul.o  sub.c  sub.o
[root@localhost src]# ar rcs libMytest.a *.o
[root@localhost src]# ls
add.c  add.o  div.c  div.o  libMytest.a  mul.c  mul.o  sub.c  sub.o
此时已经生成了静态库文件libMytest.a,该库文件包含了4个库函数add.o、div.o 、mul.o和sub.o。ar 工具不包含在gcc中,r --> 将文件插入静态库中;c --> 创建静态库,不管库是否存在(存在就不创建);s --> 写入一个目标文件索引到库中,或者更新一个存在的目标文件索引(即方便找到需要的库函数)。ar类似于命令,rcs是三个参数。

(3)使用静态库

        将生成的libMytest.a库文件和头文件head.h发送给客服,客服就可以根据头文件中的接口情况,来知道库文件的功能 (具体怎么实现的他也不知道),从而客服就可以使用库文件来完成自己的工作了(main.c):

[root@localhost Calc]# gcc -pedantic -pipe -Wall main.c -I include/ -L src/ -lMytest -o zsx    也等价于:
[root@localhost Calc]# gcc -pedantic -pipe -Wall main.c -I include/ src/libMytest.a -o zsx
[root@localhost Calc]# ./zsx 
sum = 26
分析main.c可以知道,还另外有一个头文件stdio.h,其对应的是printf函数的声明,其库文件是printf函数实现的库文件,该库文件有C提供,它们是标准头、库文件,因此不需要指明路径和名称,且该库函数为动态库函数,当程序在运行过程中需要该库函数时,才会根据头文件找到相应的库函数并加载进入内存空间。而,libMytest.a不一样,为静态库文件,因此在载入内存之前就已经链接在了一起成为程序代码的一部分。注意:main函数只是用了add函数,因此在链接时只是链接了add.o库函数,并非链接了整个库文件,只需要链接使用到的库函数即可。 例如,一个库文件libZsx.a包含了qw.o(其有a( )和b( )两个函数的实现)、qe.o(其有c( )和d( )两个函数的实现)和qr.o(其有e( )和f( )两个函数的实现)。当mani函数只是用到a( )和d( )两个函数时,则最终在链接生成可执行程序时,只是会链接qw.o和qe.o两个库函数,即main函数和这两个库函数都在代码区。

        库函数与可执行程序只是差最后一步,都具有前面是哪个阶段,且各种规定都完全一样,都是二进制代码。

        调试通过之后,加上-O参数,对代码进行优化,可以减小最后可执行文件的体积。

(3)静态库的优缺点

nm命令可以查看静态库文件(.a)和最后生成的可执行文件的详细属性。nm ibMytest.a  可以查看该静态库文件有哪些库函数(.o文件)。   nm zsx 显示的信息中有:

000000000040056c T add

0000000000400530 T main  //main函数在链接的时候加进去的启动代码

参数T代表add和main在代码区。即参数T表示在代码区的内容。

静态库优点:1.发布程序的时候不需要提供对应的库(动态库需要);2.加载库的速度快(库函数就在代码区)。缺点:库被打包到代码中,增加了代码的体积;2.库一旦发生了改变,需要对整个程序进行重新编译。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/385438.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

虚拟地址空间

对于每一个进程都会对应一个虚拟地址空间&#xff0c;对于32位的操作系统&#xff08;其指令的位数最大为32位&#xff0c;因此地址码最多32位&#xff09;&#xff0c;虚拟地址空间的大小为B即0~4GB的虚拟地址空间&#xff0c;其中内核空间为1GB&#xff0c;如下所示&#xff…

动态库(共享库)的制作和使用

Linux下的动态库为lib*.so格式的二进制文件&#xff08;目标文件&#xff09;&#xff0c;对应于Windows下的.dll格式的文件。 &#xff08;1&#xff09;命名规则 lib库名.so &#xff08;2&#xff09;动态库的制作 1&#xff09;生成与位置无关的代码&#xff08;.o&…

网络编程套接字API

uint32_t htonl(uint32_t hostlong); uint16_t htons(uint16_t hostshort); uint32_t ntohl(uint32_t netlong); uint16_t ntohs(uint16_t netshort);int inet_pton(int family, const char *strptr, void *addrptr); 分析&#xff1a; 第一个参数可以是AF_INET或AF_INET6&am…

gdb调试器(三)

File/file 装入想要调试的可执行文件 run(r) 执行当前被调试的程序 kill(k) 终止正在调试的程序 quit(q) 退出gdb shell 使用户不离开gdb就可以执行Linux的shell命令 backtrace(bt) 回溯跟踪&#xff08;当对代码进行调试时&#xff0c;run后…

makefile文件的书写规则(make和makefile)

对于makefile&#xff0c;掌握一个规则&#xff0c;两个变量和三个函数。下面介绍一个规则。 makefile的作用&#xff1a;一个项目代码的管理工具。当一个项目的代码文件数&#xff08;如.c文件&#xff09;太多&#xff0c;用gcc编译会太麻烦&#xff0c;如果全部文件一次性编…

makefile的两个变量(自动变量和普通变量)

(1)普通变量 如&#xff1a; objmain.o add.o sub.o mul.o div.o //将后面的值赋值给obj&#xff0c;obj就是一个普通变量 targetzsx //将zsx赋值给target makefile中已经定义的一些普通变量&#xff08;通常格式都是大写&#xff0c;类似环境变量&#xff0c;它们都是普通…

【C++ Priemr | 15】虚函数表剖析(二)

一、多重继承&#xff08;无虚函数覆盖&#xff09; 下面&#xff0c;再让我们来看看多重继承中的情况&#xff0c;假设有下面这样一个类的继承关系。注意&#xff1a;子类并没有覆盖父类的函数。 测试代码&#xff1a; class Base1 { public: virtual void f() { cout <…

makefile中的两个函数(wildcard和patsubst)

(1) wildcard函数 作用是查找指定目录下指定类型的文件&#xff0c;并最终返回一个环境变量&#xff0c;需要用$取值赋值给另一个环境变量&#xff01;该函数只有一个参数&#xff0c;如取出当前目录下的所有.c文件&#xff0c;并赋值给allc普通变量&#xff1a; allc$(wildc…

C库函数

Linux的系统I/O函数&#xff08;read、write、open、close和 lseek等&#xff09;与C语言的C库函数&#xff08;libc.so库文件中&#xff09;都是相对应的&#xff0c;它们都是动态库函数。如下图所示&#xff0c;C库函数有fopen、fclose、fwrite、fread和fseek等。这些C库函数…

C库函数与Linux系统函数之间的关系

由上小节知道&#xff0c;C库函数是借助FILE类型的结构体来对文件进行操作的&#xff0c;其本身只是在用户空间&#xff08;I/O缓冲区&#xff09;进行读写操作&#xff0c;而数据在内核与用户空间之间的传递、以及将内核与I/O设备之间的数据传递都是该C库函数进行一系列的系统…

open函数和errno全局变量

&#xff08;1&#xff09;open函数 man man 查看man文档的首页 其中DESCRIPTION部分描述了man文档的每一章的章节内容 第2章System calls为系统调用&#xff0c;即Liunx系统函数。 man 2 open 查看第二章的open函数的详细帮助文件。 open函数用于打开一个已经的文件或者创…

open函数和close函数的使用

学习几个常用的Linux系统I/O函数&#xff1a;open、close、write、read和lseek。注意&#xff0c;系统调用函数必须都考虑返回值。 &#xff08;1&#xff09;open函数的使用 首先&#xff0c;需要包含三个头文件&#xff1a;<sys/types.h> <sys/stat.h> <…

1091. Acute Stroke (30)

One important factor to identify acute stroke (急性脑卒中) is the volume of the stroke core. Given the results of image analysis in which the core regions are identified in each MRI slice, your job is to calculate the volume of the stroke core. Input Speci…

stat函数(stat、fstat、lstat)

#include <sys/types.h> #include <sys/stat.h> #include <unistd.h> //需包含头文件 有如下三个函数的函数原型&#xff1a; int stat(const char *path, struct stat *buf); 第一个形参&#xff1a;指出文件&#xff08;文件路径&#xff09;&…

【Leetcode | 235】 235. 二叉搜索树的最近公共祖先

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个结点 p、q&#xff0c;最近公共祖先表示为一个结点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也可以是它自己…

CPU和MMU(内存管理单元)

CPU的架构&#xff1a;要求能够理解从源程序到微指令的整个经历过程&#xff1a;存储器的层次结构&#xff08;网络资源下载到硬盘、磁盘缓存、内存、Cache、寄存器&#xff09;&#xff1b;CPU的四大部分&#xff1a;ALU、CU、中断系统和寄存器&#xff1b;程序执行的整个过程…

【C++ Primer | 09】容器适配器

一、stack s.push(): 向栈内压入一个成员&#xff1b; s.pop(): 从栈顶弹出一个成员&#xff1b; s.empty(): 如果栈为空返回true&#xff0c;否则返回false&#xff1b; s.top(): 返回栈顶&#xff0c;但不删除成员&#xff1b; s.size(): 返回栈内元素…

进程控制块PCB(进程描述符)

&#xff08;1&#xff09;PCB 每个进程在内核中都有一个进程控制块&#xff08;PCB&#xff09;来维护进程相关的信息&#xff0c;Linux内核的进程控制块是task_struct结构体。grep -r “task_struct” / 可以查找根目录下&#xff0c;包含task_struct的文件文件。或者 find…

【C++ Primer | 19】控制内存分配

1. 测试代码&#xff1a; #include <iostream> #include <new> #include <cstring> #include <cstdlib> using namespace std;void* operator new(size_t size) {cout << "global Override operator new" << endl;if (void* p…

【第15章】虚函数

一、为什么基类中的析构函数要声明为虚析构函数&#xff1f; 直接的讲&#xff0c;C中基类采用virtual虚析构函数是为了防止内存泄漏。具体地说&#xff0c;如果派生类中申请了内存空间&#xff0c;并在其析构函数中对这些内存空间进行释放。假设基类中采用的是非虚析构函数&am…