添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
ar -r libCAdd .a CAdd .o # 归档生成libCAdd.a

编译动态库 libCAdd.so

gcc -shared -o libCAdd.so CAdd.c

cppmain.cpp

#include <stdio.h>
extern "C" {
#include "CAdd.h"
int main()
  int sum = cadd(1, 2);
  printf("1+2 = %d\n", sum);
  return 0;

编译main
-l指定库名称,优先链接so动态库,没有动态库再链接.a静态库。

g++ -o cppmain cppmain.cpp -L. -lCAdd

运行
如果链接的是静态库就可以直接运行了,如果链接的是动态库可能会提示
./cppmain: error while loading shared libraries: libCAdd.so: cannot open shared object file: No such file or directory,是因为Linux系统程序和Windows不一样,Linux系统只会从系统环境变量指定的路径加载动态库,可以把生成的动态库放到系统目录,或者执行export LD_LIBRARY_PATH=./设置当前路径为系统链接库目录就可以了。

注释
这里是在include头文件的外面包裹了extern "C" { },是告诉编译器以C语言的命名方式去加载这个符号。还有一种比较常见的方式是在头文件中进行编译声明,如下所示,这样的话,无论C还是C++直接正常include就可以使用了。

CAdd.h

#ifdef __cplusplus
extern "C" {
#endif
int cadd(int x, int y);
#ifdef __cplusplus
#endif

C调用C++的静态库

C语言没法直接调用C++的函数,但可以使用包裹函数来实现。C++文件.cpp中可以调用C和C++的函数,但是C代码.c只能调用C的函数,所以可以用包裹函数去包裹C++函数,然后把这个包裹函数以C的规则进行编译,这样C就可以调用这个包裹函数了。

CppAdd.h

int cppadd(int x, int y);

CppAdd.cpp

#include "CppAdd.h"
#include <stdio.h>
int cppadd(int x, int y) {
    printf("from C++ function.\n");
    return (x + y);

编译静态库 libCppAdd.a

g++ -c CppAdd.cpp
ar -r libCppAdd.a CppAdd.o

CppAddWrapper.h

#ifdef __cplusplus
extern "C" {
#endif
int cppaddwrapper(int x, int y);
#ifdef __cplusplus
#endif

CppAddWrapper.cpp

#include "CppAddWrapper.h"
#include <stdio.h>
#include "CppAdd.h"
int cppaddwrapper(int x, int y) {
    printf("from wrapper.\n");
    int sum = cppadd(x, y);
    return sum;

编译wrapper静态库 libCppAddWrapper.a

g++ -c CppAddWrapper.cpp
ar -r libCppAddWrapper.a CppAddWrapper.o

main.c

#include "CppAddWrapper.h"
#include <stdio.h>
int main()
  int sum = cppaddwrapper(1, 2);
  printf("1+2 = %d\n", sum);
  return 0;

编译main,同时指定libCppAdd.a 和 libCppAddWrapper.a。

gcc -o main main.c -L. -lCppAddWrapper -lCppAdd

或者把libCppAdd.a合并到libCppAddWrapper.a中

ar -x libCppAdd.a         # 提取CppAdd.o
ar -x libCppAddWrapper.a  # 提取CppAddWrapper.o
ar -r libCppAddWrapper.a CppAdd.o CppAddWrapper.o # 打包libCppAddWrapper.a
gcc -o main main.c -L. -lCppAddWrapper  # 只需要连接libCppAddWrapper.a即可

如果是C调用C++的so动态库的话,类似于调用静态库的方法应该也是有效的,太麻烦我没试过。

C/C++函数符号的区别

C++可以兼容C的语法,C/C++主要的区别是编译函数符号规则不一样,C语言代码编译后的函数名还是原来函数名,C++代码编译后的函数名带有参数信息。
做个测试来检验一下。一个简单的函数,分别用C和C++进行编译。
hello1.c

int test(int a, char* b){
    return a;

hello2.cpp

int test(int a, char* b){
    return a;
gcc -c hello1.c     # 生成hello1.o
g++ -c hello1.cpp   # 生成hello2.o

查看符号表

$ nm hello1.o
0000000000000000 T test
$ nm hello2.o
0000000000000000 T _Z4testiPc

从上面信息可以看出,C语言编译后的函数符号还是原函数名,而C++编译后的函数符号由test变成了_Z4testiPc,从这个符号名字可以看出test前面有个数字4应该是函数名长度,test后面i Pc应该就是函数的参数签名。C++之所以这样规定编译后的函数符号是因为对面对象的C++具有函数重载功能,以此来区分不同的函数。

.so动态库、.a静态库和.o中间文件的关系

程序的运行都要经过编译和链接两个步骤。假如有文件add.c,可以使用命令gcc -c add.c进行编译,生成add.o中间文件,使用命令ar -r libadd.a add.o可以生成libadd.a静态库文件。静态库文件其实就是对.o中间文件进行的封装,使用nm libadd.a命令可以查看其中封装的中间文件以及函数符号。
链接静态库就是链接静态库中的.o文件,这和直接编译多个文件再链接成可执行文件一样。
动态链接库是程序执行的时候直接调用的“插件”,使用命令gcc -shared -o libadd.so add.c生成so动态库。动态库链接的时候可以像静态库一样链接,告诉编译器函数的定义在这个静态库中(避免找不到函数定义的错误),只是不把这个so打包到可执行文件中。如果没有头文件的话,可以使用dlopen/dlsum函数手动去加载相应的动态库。详细做法参考上一篇文章《C语言调用so动态库的两种方式》。

C调用C++C++调用C方法 https://blog.csdn.net/shaosunrise/article/details/81176880?ops_request_misc=&request_id=&biz_id=102&utm_term=c%E5%A6%82%E4%BD%95%E8%B0%83%E7%94%A8C++%E7%9A%84%E5%BA%93&utm_medium=distribute.pc_search_result.none-task-blog JNA(Java Native Access )提供一组Java工具类用于在运行期间动态访问系统本地(native library:如Window的dll)而不需要编写任何Native/JNI代码。开发人员只要在一个java接口中描述目标native library的函数与结构,JNA将自动实现Java接口到native function的映射。... 在C语言中值传递、指针传递和引用传递这三种函数参数传递方式是比较基本的知识,用的比较普遍,但不仔细分析其实质的话,时间长容易记混了。网上的资料也较多但多数都不系统,本文力求用最白话的表达和简单的示例把这三种方式描述清楚。没时间看分析的直接看简述就可以了。 值传递就是最普通的传递方式,比如函数定义为fun(int a),在调用的地方有int x=6, 使用fun(x)就可以了。这种方式在 先从一个简单的场景来理解什么叫I/O复用。学习过UNIX TCP网络编程的同学肯定知道accept和recv是阻塞的函数,accept函数是等待客户端连接,接受连接后返回,继续执行recv函数等待读取客户端发送过来的请求。但是如果一直没有客户端连接程序就会卡在accept函数上,连接后如果客户端没有数据发送就会卡在recv函数上,没法做别的事情。在处理一个客户端连接的时候也没法监听新的连接。也就是...