Linux中g++将C++代码编译为动态库.so文件

Linux中g++将C++代码编译为动态库.so文件

前言

GCC(GNU Compiler Collection)是由GNU项目开发的编程语言编译器,支持多种编程语言,如C、C++、Objective-C、Fortran、Ada、Go等。gcc主要负责将源代码编译成机器码或中间代码。
Make是一种在Unix和类Unix操作系统中广泛使用的自动化构建工具,它根据Makefile(或称为makefile)文件中定义的规则来自动编译和链接程序。

  • gcc:直接对单个或多个源文件进行编译,生成目标文件或可执行文件。

  • make:通过分析Makefile文件中的指令,自动判断哪些文件需要被重新编译,并执行相应的编译命令。它支持增量编译,即只重新编译那些自上次编译以来被修改过的文件,从而提高编译效率。

  • gcc:通常用于编译单个文件或少量文件的项目。

  • make:更适用于大型项目,特别是那些包含大量源文件且源文件之间存在复杂依赖关系的项目。通过Makefile,可以清晰地定义项目结构,简化编译过程。

目标:将cpp文件生成动态库so文件

在Linux中,想要将C++代码编译成动态链接库(.so文件,即Shared Object),需要使用编译器(如g++)的-shared和-fPIC选项。这些选项会告诉编译器生成一个位置无关的代码(Position Independent Code, PIC),这是共享库所必需的,以便它们可以在不同的内存地址加载。

以下是一个简化的过程,说明如何将你的代码编译成.so文件:

假设你有以下文件:只有一个头文件和cpp

header1.h:包含Class1的声明
source1.cpp:包含Class1的实现

header1.h

cpp 复制代码
#ifndef HEADER1_H  
#define HEADER1_H  
  
class Class1 {  
public:  
    void method1();  
};  
  
#endif
source1.cpp

source1.cpp

cpp 复制代码
#include "header1.h"  
#include <iostream>  
  
void Class1::method1() {  
    std::cout << "Class1's method1 called" << std::endl;  
}

单个cpp文件编译成.so文件命令:

打开你的终端(命令行界面),然后使用g++编译你的代码,并指定输出为.so文件,此处只需要将其名称改为你的文件名:

bash 复制代码
g++ -shared -fPIC -o libclass1.so source1.cpp

这里,-shared选项告诉g++生成一个共享对象文件,而-fPIC选项则确保生成的代码是位置无关的。-o libclass1.so指定了输出文件的名称。注意,按照惯例,共享库文件通常以lib开头,但这并不是强制性的。
注意:包含的.h头文件一般跟cpp放于同一路径,编译器会自己查找,在命令行中并不用包含.h头文件名称等等

假设你有以下文件:有两个或多个头文件和cpp

你想要创建动态链接库(.so文件),你可以直接将这两个.cpp文件编译成.so文件,而无需先生成目标文件(尽管在某些情况下,你可能仍然希望分步骤进行,以便于调试或并行编译)。
header1.h:包含类Class1的声明
source1.cpp:包含类Class1的实现
header2.h:包含类Class2的声明
source2.cpp:包含类Class2的实现

多个cpp文件编译成.so文件命令:

在终端(命令行界面)中,使用g++(或你选择的任何C++编译器)编译这两个.cpp文件,并生成一个.so文件。这里是一个例子:

bash 复制代码
g++ -shared -fPIC -o libmylibrary.so source1.cpp source2.cpp

这条命令会编译source1.cpp和source2.cpp,并将它们链接成一个名为libmylibrary.so的共享库文件。-shared选项告诉g++生成一个共享对象,而-fPIC选项则确保生成的代码是位置无关的。

注意事项

  • 确保你的头文件(header1.h和header2.h)被正确地包含在你的.cpp文件中。
  • 如果你的类使用了模板或需要在头文件中实现某些成员函数(例如模板类的成员函数或内联函数),请确保这些实现放在头文件中,或者使用inline关键字在.cpp文件中实现它们(但请注意,对于模板来说,通常推荐将声明和实现都放在头文件中,或者使用隐式模板实例化等技术)。
  • 如果你在编译过程中遇到未定义的引用错误,可能是因为某个函数或变量在.cpp文件中声明了但在任何.cpp文件中都没有定义,或者某个.cpp文件没有包含它需要的所有头文件。
  • 在使用生成的.so文件时,确保你的程序在链接时能够找到它。这通常意味着你需要在编译时通过-L和-l选项指定库的位置和名称,或者将库文件放在标准库路径下,并更新系统的库缓存(如果使用的是像/usr/lib或/usr/local/lib这样的标准库路径的话)。

使用.so文件

一旦你有了.so文件,你就可以在其他C++程序中通过动态链接来使用它。为了做到这一点,你需要在编译时指定库文件的路径(使用-L选项)(这里的L其实就是Lib的首字母)和库名称(使用-l选项,注意省略lib前缀和.so后缀,这里即class1)。此外,你还需要在代码中包含相应的头文件。

例如,如果你有一个使用Class1的程序main.cpp,你可以这样编译它:

bash 复制代码
g++ main.cpp -L. -lclass1 -o my_program

注意,这里-L.告诉编译器在当前目录下查找库文件。如果你的库文件位于其他目录,你需要将.替换为库文件所在的目录路径。

另外,确保在运行你的程序时,动态链接器(ld.so)能够找到你的.so文件。这通常通过设置LD_LIBRARY_PATH环境变量来完成,或者将你的库文件安装到标准库路径下。例如:

bash 复制代码
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.  
./my_program

或者,将库文件安装、复制到如/usr/local/lib这样的标准库路径下,并确保运行

bash 复制代码
ldconfig  //来更新系统的库缓存。

这样的好处就是你不用写库路径,它会自己去标准库路径下找。

相关推荐
Oneforlove_twoforjob几秒前
【Java基础面试题033】Java泛型的作用是什么?
java·开发语言
watermelonoops5 分钟前
Windows安装Ubuntu,Deepin三系统启动问题(XXX has invalid signature 您需要先加载内核)
linux·运维·ubuntu·deepin
engchina16 分钟前
如何在 Python 中忽略烦人的警告?
开发语言·人工智能·python
向宇it17 分钟前
【从零开始入门unity游戏开发之——C#篇24】C#面向对象继承——万物之父(object)、装箱和拆箱、sealed 密封类
java·开发语言·unity·c#·游戏引擎
诚丞成42 分钟前
计算世界之安生:C++继承的文水和智慧(上)
开发语言·c++
Smile灬凉城6661 小时前
反序列化为啥可以利用加号绕过php正则匹配
开发语言·php
滴水之功1 小时前
VMware OpenWrt怎么桥接模式联网
linux·openwrt
lsx2024061 小时前
SQL MID()
开发语言
Dream_Snowar1 小时前
速通Python 第四节——函数
开发语言·python·算法
西猫雷婶1 小时前
python学opencv|读取图像(十四)BGR图像和HSV图像通道拆分
开发语言·python·opencv