Qt Creator支持编译生成c++库,打开新建工程,根据创建向导,能够很快生成c++共享库的工程
创建向导上显示的是c++的库,c语言的库也同样能够生成,下面对生成c语言的共享库做个介绍
生成c共享库
1.首先按生成c++库的方式生成工程,过程就不罗列了,根据要求自行选择
创建完成后,生成以下几个文件
其中testclib是默认生成的类,TestClib_global.h是生成库需要的宏
2.把testclib.cpp后缀改为.c
3.删掉默认的类,添加自己的接口,每个接口前面添加导出库的宏,把global头文件内的宏拷贝到接口头文件中,这样后续调用不用包含global头文件了,如果是有多个.h的接口,建议还是保留global的方式
#ifndef TESTCLIB_H
#define TESTCLIB_H
#if defined(_MSC_VER) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
# define Q_DECL_EXPORT __declspec(dllexport)
# define Q_DECL_IMPORT __declspec(dllimport)
#else
# define Q_DECL_EXPORT __attribute__((visibility("default")))
# define Q_DECL_IMPORT __attribute__((visibility("default")))
#endif
#if defined(TESTCLIB_LIBRARY)
# define TESTCLIB_EXPORT Q_DECL_EXPORT
#else
# define TESTCLIB_EXPORT Q_DECL_IMPORT
#endif
TESTCLIB_EXPORT int add(int a, int b);
#endif // TESTCLIB_H
#include "testclib.h"
int add(int a, int b)
{
return a + b;
}
4.执行编译,在编译生成目录即生成相应的库,可以看到生成4个.so文件
其中libTestClib.so libTestClib.so.1 libTestClib.so.1.0都是libTestClib.so.1.0.0软链接
为什么会有这么多个,想要知道其中的原因,首先来了解Linux共享库的命名规则
Linux共享库的命名规则
libname.so.x.y.z
前缀lib,中间是库的名字和后缀.so,后面的三个数字组成版本号,“x”表示主版本号,“y”表示此版本号,“z”表示发布版本号,这个三个版本号的具体含义:
- 主版本号:表示库的重大升级,不同版本号的库之间是不兼容的,依赖与旧的版本号的程序要改动相应的部分,并且重新编译。才可以在新版的共享库中运行,或者系统必须保留旧版的共享库,使得那些依赖于旧版共享库的程序能够正常运行
- 次版本号:表示库的增量升级,即增加一些新的接口符号,并保持原有的符号不变,在主版本号相同的情况下,高的此版本号的库向后兼容低的次版本好的库。
- 发布版本号:表示库的一些错误的修正和性能的改进等,并不添加任何新的接口,也不对接口进行更改,相同主,次版本好的共享库,不同发布版本号之间完全兼容。
也有部分不遵守上述规定的共享库,如glibc,它的c语言库的命名就是libc-x.y.z.so,glibc包含许多组件,C语言库只是其中一个,动态链接器也是glibc的一部分 ,它的命名:
ld-x.y.z.so
编译使用共享库时,例如GCC 使用 -l 参数链接共享库libxxx.so.2.1.1,只需要-lxxx,xxx 就是共享库的连接名,编译器会根据当前环境,在相关路径查找最新版本的xxx库。
所以使用时我们需要libTestClib.so、libTestClib.so.1、libTestClib.so.1.0.0这三个库,其中libTestClib.so用来对应-lxxx的名称,-l的链接方式无法对应库名称带了版本号的共享库。libTestClib.so.1是libTestClib.so.1.0.0的soname,指定了soname后,ld链接器链接时都是通过soname的库进行链接,这样做是方便兼容,如果主版本库不变,比如更新了libTestClib.so.1.2.2,那只需让libTestClib.so.1重新指向libTestClib.so.1.2.2即可,程序还是通过libTestClib.so.1进行调用,关于soname的使用,可以查阅相关资料进一步了解,这里简单做个说明。
使用命令查看libTestClib.so.1.0.0
readelf -d libTestClib.so.1.0.0
可以看到libTestClib.so.1.0.0的soname为libTestClib.so.1
隐式调用共享库
>新建一个c工程
>在工程目录下新建lib文件夹,把上面提到的三个库复制出来,当然直接四个都拷也没事
>添加头文件到工程中
>Pro文件链接库
LIBS += -L$$PWD/lib –lTestClib
qmake +编译即可
调用的时候通过尝试只使用libTestClib.so.1 libTestClib.so.1.0.0, 会提示找不到库文件
:-1: error: cannot find –lTestClib
:-1: error: collect2: error: ld returned 1 exit status
通过只调用libTestClib.so.1.0.0,并改名为libTestClib.so,链接编译能够通过,但运行时会报错找不到共享库libTestClib.so.1
error while loading shared libraries: libTestClib.so.1: cannot open shared object file: No such file or directory
使用-l的方式链接,带有soname的共享库目前发现只能把三个都拷贝过来才能使用
显示调用共享库
如果是使用dlopen通过显示的方式加载共享库,那就没有上述的问题,只使用libTestClib.so.1.0.0就可以了,当然用libTestClib.so.1也可以,但libTestClib.so.1.0.0也还需要保留
#include <stdio.h>
#include "testclib.h"
#include <dlfcn.h>
typedef int (*testAdd) (int, int);
int main()
{
printf("Hello World!\n");
void * handle = dlopen("xxx/libTestClib.so.1", RTLD_NOW);
if(handle == NULL) {
return -1;
}
testAdd add = dlsym(handle,"add");
int ret = add(1, 2);
printf("add: %d", ret);
return 0;
}
pro文件还需链接dl库
LIBS += -ldl
不然会报错找不到dlopen
采用非soname的方式
使用Qt Creator编出来的库采用的是soname这种方式,我们也可以自己用命令行生成libname.so的形式,而不区分什么主版本次版本
在Qt Creator下方输出栏找到编译输出窗口
可以看到执行的命令
g++ -shared -Wl,-soname,libTestClib.so.1 -o libTestClib.so.1.0.0 testclib.o
去掉soname参数
g++ -shared -o libTestClib.so testclib.o
因为已经编译过生成.o文件,在编译目录下执行上述命令即可生成libTestClib.so
这样只需使用libTestClib.so,把之前那三个库删掉,使用新生成的libTestClib.so一样能够使用
如果没特殊要求按这种方式也可以,毕竟我们的宗旨是能用能运行就对了
结语
通过查看/usr/lib下的库基本都是用第一种方式,这种方式目的就是解决库更新冲突、兼容问题,对于程序链接这一块我也不是很熟,看自己的需要来选择就好了。
以上是Linux下c语言生成共享库的方法,c++步骤也是一样的,只不过c++一般使用类的方式,按照Qt Creator向导生成创建即可。Qt是跨平台的,只要处理好平台相关的代码,同一个代码工程在windows下也能直接编译生成windows的库