Linux-(C/C++)生成并使用静态库/动态库

本文介绍了静态库和动态库的概念及区别,详细讲解了在Linux环境下如何生成和使用这两种类型的库,包括通过g++手动创建及利用cmake工具简化过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 静态库/动态库概要

在Windows下静态库的后缀为:.lib、动态库后缀为:.dll;而在Linux下静态库的后缀为:.a、动态库的后缀为:.so。

那么什么是静态库呢?

首先我们来看看程序编译的大体流程:预处理——编译——汇编——链接

在我们的项目开发中,有些代码会被反复使用,那么这时我们便可将这些代码编译成库的形式来调用,像静态库就是在可执行文件中包含库代码的一份完整拷贝,但这种方式有个很严重的缺点就是会造成多份冗余拷贝。静态库会在程序的链接阶段被复制到程序中,所以程序在运行后是不会调用静态库的。

那动态库又是什么呢?

动态库不像静态库那样,在链接阶段并没有被复制到程序中,反而是程序运行的时候由系统动态的加载到内存中供程序调用,所以这里解决了静态库早晨多份冗余拷贝的缺点,系统只需要载入一次动态库,不同的程序可以得到内存中相同的动态库副本,因此可以节省大量的内存。

 

  • 生成静态库/动态库

下面进入主题看看在linux中如何生成静态库跟动态库:

首先我们先创建三个文件,分别为hello.cc、hello.h、main.cc,如下图:

 

“hello.h":


 
  1. #ifndef __HELLO_H__

  2. #define __HELLO_H__

  3.  
  4. #include <stdio.h>

  5.  
  6. void Hello();

  7.  
  8. #endif

"hello.cc":


 
  1. #include "hello.h"

  2.  
  3. void Hello()

  4. {

  5. printf("Hello World!!!\n");

  6. }

"main.cc":


 
  1. #include "hello.h"

  2.  
  3. int main(void)

  4. {

  5. Hello();

  6.  
  7. return 0;

  8. }

这时我们hello world代码算是写完了。接下来我们生成静态库

g++ -o main main.cc -static -lhello -L.

 

我们先简单介绍下"gcc"跟"g++":gcc 最开始的时候是 GNU C Compiler, 如你所知,就是一个c编译器。但是后来因为这个项目里边集成了更多其他不同语言的编译器,GCC就代表 the GNU Compiler Collection,所以表示一堆编译器的合集。 g++则是GCC的c++编译器。

 

此处我们使用g++,如果使用c语言可以将g++改为gcc:

1、将hello.cc编译成hello.o(静态库和动态库都是由.o文件生成)

g++ -c hello.cc

2、为了遵循linux中静态库的命名规范,静态库命名为libhello.a

ar cr libhello.a hello.o

3、将main.cc与静态库连接,生成可执行文件:“main”

g++ -o main main.cc -static -lhello -L.

最后文件如下如:

4、运行可执行文件

./main

下面进行动态库的生成

1、生成动态库libhello.so


 
  1. g++ -c -fPIC hello.cc -o hello.o

  2. g++ -shared hello.o -o libhello.so

当然这里我们可以直接通过cmake来生成动态库,下面我们用cmake的方式生成动态库并链接动态库。

首先我们改造下目录结构:

  

我们删除上一步生成的静态库跟动态库,只保留源代码,在开始的code目录下创建两个文件夹分别是:build、lib;在code跟lib目录下分别创建一个CMakeLists.txt文件(用于编写cmake代码),并在code同级目录下创建code2目录,在该目录下创建build、src目录,将之前的main.cc移到src下,在code2目录跟src目录下创建CMakeLists.txt文件,先来看看code目录下的CMakeLists.txt文件,代码如下:

PROJECT(HELLOLIB)
ADD_SUBDIRECTORY(lib)

lib目录下的CMakeLists.txt文件代码如下:

SET(LIBHELLO_SRC hello.cc)
ADD_LIBRARY(hello SHARED ${LIBHELLO_SRC})
INSTALL(TARGETS hello hello
LIBRARY DESTINATION lib)
INSTALL(FILES hello.h DESTINATION include/hello)

对上面代码解析下,ADD_LIBRARY用于生成库文件,hello是动态库名(libxxx.x中xxx的名字),SHARED表示生成的库为动态库。之后的INSTALL是安装共享库和头文件,安装到系统库中(/usr/lib跟usr/include/hello目录下,这里的hello文件夹是我自己创建的)。

最后在build目录下运行终端(在执行INSTALL时需要用到root权限,所以在执行make前先切换到root模式下),输入命令行如下:

cmake -DCMAKE_INSTALL_PREFIX=/usr ..
make
make install

可以到指定目录中查看库跟头文件已经成功安装到了指定目录下。

下面我们在main.cc中调用我们生成的动态库。

首先我们先将main.cc中引入的头文件#include "hello.h"改为#include <hello.h>;

我们先看看code2目录下的CMakeLists.txt下的脚本:

PROJECT(NEWHELLO)
ADD_SUBDIRECTORY(src)

src目录下的CMakeLists.txt脚本如下:

ADD_EXECUTABLE(main main.cc)
INCLUDE_DIRECTORIES(/usr/include/hello)
TARGET_LINK_LIBRARIES(main libhello.so)

跟上面一样我们在终端下来到build目录,并输入命令行如下:

cmake ..
make

得到的输出为:Hello World!!!

到这里我们链接库的实例就说完了。cmake是个好东西!你值得拥有!

文中是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、dlsymdlclose就够了),但对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.x3.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、付费专栏及课程。

余额充值