Linux系统下gcc生成静态库和动态库及实例

本文详细介绍了在Linux环境下如何使用GCC生成及使用静态库和动态库。通过hello实例,展示了从创建.o文件到构建静态库.a和动态库.so的步骤,并在主程序中调用库函数进行验证。此外,还提供了额外的实例,演示了静态库与动态库的生成和使用,以及两者在文件大小上的差异。最后,文章指出在使用动态库时需要将其复制到系统库路径下才能正常运行。
摘要由CSDN通过智能技术生成

一、前言

我们通常把一些公用函数制作成函数库,供其他程序使用,函数库分为静态库动态库两种。
静态库在程序编译时会连接到目标代码中,程序运行时将不再需要该静态库。
动态库在程序编译时并不会被连接到目标代码中,而是在程序运行时才被载入。
本文主要通过举例来说明在Linux中如何创建静态库动态库,以及使用它们。

二、用gcc生成静态库和动态库

(一).hello实例

1.准备过程

(1) .创建一个作业目录,保存本次练习的文件

#mkdir test1
#cd test1

(2) .程序代码
hello.h为该函数库的头文件

#ifndef HELLO_H
#define HELLO_H
void hello(const char *name)
#endif //HELLO_H

hello1.c是函数库的源程序,其中包含公用函数hello,该函数将在屏幕上输出“Hello,XXX!"。

#include <stdio.h>
void hello(const char *name)
{
printf("Hello %s!\n",name);
}

main.c为测试库文件的主程序,在主程序中调用了公用函数hello

#include "hello.h"
int main()
{
hello("everyone");
return 0;
}

(3) .将hello.c编译成.o文件
在系统提示符下键入以下命令得到hello.o文件

gcc -c hello.c

运行ls命令看看是否生成了hello.o文件

ls

在这里插入图片描述
看到有hello.o文件,说明本部操作完成。

2.静态库的使用

(1) .由.o文件创建静态库
静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名.a。

ar -crv libmyhello.a hello.o

运行ls命令看看是否生成了静态库文件文件

ls

在这里插入图片描述
看到libmyhello.a文件,说明本部操作完成。

(2) .在程序中使用静态库
静态库制作完了,如何使用它内部的函数呢?只需要在使用到这些公用函数的源程序中包 含这些公用函数的原型声明,然后在用 gcc 命令生成目标文件时指明静态库名,gcc 将会从 静态库中将公用函数连接到目标文件中。注意,gcc 会在静态库名前加上前缀 lib,然后追 加扩展名.a 得到的静态库文件名来查找静态库文件。 在程序 main.c 中,我们包含了静态库的头文件 hello.h,然后在主程序 main 中直接调用 公用函数 hello。下面先生成目标程序 hello,然后运行 hello 程序看看结果如何。

方法一:

 gcc -o hello main.c -L. –lmyhello 

自定义的库时,main.c 还可放在-L.和 –lmyhello 之间,但是不能放在它俩之后,否则会提 示 myhello 没定义,但是是系统的库时,如 g++ -o main(-L/usr/lib) -lpthread main.cpp 就不出错。

方法二:

gcc main.c libmyhello.a -o hello

方法三:
先生成 main.o:

gcc -c main.c 

再生成可执行文件:

gcc -o hello main.o libmyhello.a

动态库连接时也可以这样做。

输入./hello
在这里插入图片描述
说明操作成功了。
然后我们删除静态库文件试试公用函数hello是否真的连接到目标文件hello中了

rm libmyhello.a
./hello

在这里插入图片描述
可以看到libmyhello.a已经被删除,而程序照常运行,说明静态库中的公用函数已经连接到目标文件中。

3.动态库的使用

(1) .由.o文件创建动态库文件
动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀 lib,但其 文件扩展名为.so。例如:我们将创建的动态库名为 myhello,则动态库文件名就是 libmyh ello.so。用 gcc 来创建动态库。
在系统提示符下键入以下命令得到动态库文件 libmyhello.so。

gcc -shared -fPIC -o libmyhello.so hello.o

提示错误

这是因为在创建hello.o文件时没有键入-fPIC使不一致导致的
重新输入

gcc -o -fPIC hello.c

之后再输入

gcc -shared -fPIC -o libmyhello.so hello.o

用ls命令查看,发现动态库已经创建成功
在这里插入图片描述
(2) .在程序中使用动态库
在程序中使用动态库和使用静态库完全一样,也是在使用到这些公用函数的源程序中包含 这些公用函数的原型声明,然后在用 gcc 命令生成目标文件时指明动态库名进行编译。程序在运行时, 会在/usr/lib 和/lib 等目录中查找需要的动态库文件。若找到,则载入动态库,否则将提 示类似上述错误而终止程序运行。我们将文件 libmyhello.so 复制到目录/usr/lib 中在运行,否则会报错。

gcc -o hello main.c -L. -lmyhello

之后输入./hello看程序是否执行

./hello

在这里插入图片描述
果然出现了错误。
解决方法是将文件 libmyhello.so 复制到目录/usr/lib 中在运行
输入mv libmyhello.so 提示权限不够
在这里插入图片描述
所以我们需要临时权限提高
输入sudo mv libmyhello.so就可以解决
之后输入./hello,程序成功运行,如下图所示
在这里插入图片描述

(二).linux下静态库.a和.so库文件的生成和使用实例

1.准备过程

(1) .先创建一个作业目录,保存本次示例文件

mkdir test2
cd test2

(2) .实例代码

A1.c:

#include <stdio.h> 
void print1(int arg)
{
 printf("A1 print arg:%d\n",arg); 
}

A2.c:

#include <stdio.h>
 void print2(char *arg)
 { 
 printf("A2 printf arg:%s\n", arg);
  }

A.h:

#ifndef A_H 
#define A_H 
void print1(int);
void print2(char *);
#endif

test.c:

#include <stdlib.h>
#include "A.h" 
int main()
{
 print1(1); 
 print2("test"); 
 exit(0); 
}

2.静态库.a文件的生成与使用

gcc -c A1.c A2.c
ar crv libafile.a A1.o A2.o

使用.a 库文件,创建可执行程序(若采用此种方式,需保证生成的.a 文件与.c 文件保 存在同一目录下,即都在当前目录下)
如图都在libafile文件夹内
在这里插入图片描述

gcc -o test test.c libafile.a
./test

如图所示,说明使用成功
在这里插入图片描述

3.动态库.so文件的生成与使用

生成目标文件(xxx.o() 此处生成.o 文件必须添加"-fpic"(小模式,代码少),否则在生成.so 文件时会出错)
gcc -c -fpic A1.c A2.c
生成共享库.so 文件
gcc -shared *.o -o libsofile.so
使用.so 库文件,创建可执行程序
gcc -o test test.c libsofile.so
./test
发现出现错误:
./test: error while loading shared libraries: libsofile.so: cannot open shared object file: No such file or directory
运行 ldd test,查看链接情况

  ldd test 
  linux-vdso.so.1 => (0x00007fff0fd95000) 
  libsofile.so => not found 
  libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f937b5de000) 
  /lib64/ld-linux-x86-64.so.2 (0x0000563f7028c000)

发现确实是找不到对应的.so 文件。 这是由于 linux 自身系统设定的相应的设置的原因,即其只在/lib and /usr/lib 下搜索对应 的.so 文件,故需将对应 so 文件拷贝到对应路径。

sudo cp libsofile.so /usr/lib 

再次执行./test,即可成功运行。
全过程如图所示
在这里插入图片描述

(三).其他实例

1.准备过程

(1) .创建一个作业目录,保存本次练习的文件

#mkdir example1
#cd example1

(2) .实例代码

sub1.c:

float x2x(int a,int b)
{
	float c=0;
	c=a+b;
	return c;
}

sub2.c:

float x2y(int a,int b)
{
	float c=0;
	c=a/b;
	return c;
}

sub.h:

#ifndef SUB_H
#define SUB_H
float x2x(int a,int b);
float x2y(int a,int b);
#endif

main.c:

#include<stdio.h>
#include"sub.h"
void main()
{
	int a,b;
	printf("Please input the value of a:");
	scanf("%d",&a);
	printf("Please input the value of b:");
	scanf("%d",&b);
	printf("a+b=%.2f\n",x2x(a,b));
	printf("a/b=%.2f\n",x2y(a,b));
}

2.静态库使用

gcc -c sub1.c sub2.c

在这里插入图片描述

ar crv libsub.a sub1.o sub2.o

在这里插入图片描述

gcc -o main main.c libsub.a

在这里插入图片描述
可以看到输入./main,按要求输入a,b,可以看到输出是正确的,说明程序正确执行。

3.动态库的使用

gcc -shared -fPIC -o libsub.so sub1.o sub2.o
gcc -o main main.c libsub.so
sudo mv libsub.so /usr/lib

在这里插入图片描述
可以看到输入./main,按要求输入a,b,可以看到输出是正确的,说明程序正确执行。

4.静态库与动态库的生成文件大小的比较

静态库

在这里插入图片描述

动态库
在这里插入图片描述
可以看到态库要比动态库要小很多,生成的可执行文件大小也存在较小的差别。

三、参考资料

gcc生成静态库.a和动态库.so.

文中是linuxC++动态库 实现接口提供类导出的一个例子 注意其中使用函数返回基类指针的用法,因为Linux的动态链接库不能像MFC中那样直接导出类 一、介绍 如何使用dlopen API动态地加载C++函数和类,是Unix C++程序员经常碰到的问题。 事实上,情况偶尔有些复杂,需要一些解释。这正是写这篇mini HOWTO的缘由。 理解这篇文档的前提是对C/C++语言中dlopen API有基本的了解。 这篇HOWTO的维护链接是: http://www.isotton.com/howtos/C++-dlopen-mini-HOWTO/ 二、问题所在 有时你想在运行时加载一个库(并使用其中的函数),这在你为你的程序写一些插件或模块架构的时候经常发生。 在C语言中,加载一个库轻而易举(调用dlopen、dlsym和dlclose就够了),但对C++来说,情况稍微复杂。 动态加载一个C++库的困难一部分是因为C++的name mangling (译者注:也有人把它翻译为“名字毁坏”,我觉得还是不翻译好), 另一部分是因为dlopen API是用C语言实现的,因而没有提供一个合适的方式来装载类。 在解释如何装载C++库之前,最好再详细了解一下name mangling。 我推荐您了解一下它,即使您对它不感兴趣。因为这有助于您理解问题是如何产生的,如何才能解决它们。 1. Name Mangling 在每个C++程序(或库、目标文件)中, 所有非静态(non-static)函数在二进制文件中都是以“符号(symbol)”形式出现的。 这些符号都是唯一的字符串,从而把各个函数在程序、库、目标文件中区分开来。 在C中,符号名正是函数名:strcpy函数的符号名就是“strcpy”,等等。 这可能是因为两个非静态函数的名字一定各不相同的缘故。 而C++允许重载(不同的函数有相同的名字但不同的参数), 并且有很多C所没有的特性──比如类、成员函数、异常说明──几乎不可能直接用函数名作符号名。 为了解决这个问题,C++采用了所谓的name mangling。它把函数名和一些信息(如参数数量和大小)杂糅在一起, 改造成奇形怪状,只有编译器才懂的符号名。 例如,被mangle后的foo可能看起来像foo@4%6^,或者,符号名里头甚至不包括“foo”。 其中一个问题是,C++标准(目前是[ISO14882])并没有定义名字必须如何被mangle, 所以每个编译器都按自己的方式来进行name mangling。 有些编译器甚至在不同版本间更换mangling算法(尤其是g++ 2.x和3.x)。 即使您搞清楚了您的编译器到底怎么进行mangling的,从而可以用dlsym调用函数了, 但可能仅仅限于您手头的这个编译器而已,而无法在下一版编译器下工作。 三、类 使用dlopen API的另一个问题是,它只支持加载函数。 但在C++中,您可能要用到库中的一个类,而这需要创建该类的一个实例,这不容易做到。 四、解决方案 1. extern "C" C++有个特定的关键字用来声明采用C binding的函数: extern "C" 。 用 extern "C"声明的函数将使用函数名作符号名,就像C函数一样。 因此,只有非成员函数才能被声明为extern "C",并且不能被重载。 尽管限制多多,extern "C"函数还是非常有用,因为它们可以象C函数一样被dlopen动态加载。 冠以extern "C"限定符后,并不意味着函数中无法使用C++代码了, 相反,它仍然是一个完全的C++函数,可以使用任何C++特性和各种类型的参数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值