当前位置:   article > 正文

C和C++程序互相调用方案_c调用c++

c调用c++

C++程序调用C函数

项目实际开发需求需要在C++程序中调用c语言实现的库,那么需要怎么实现呢?

原理:C++函数为了支持函数重载,g++编译器会对函数名进行修饰,而C函数编译的符号表中只是函数名。同样的代码用C编译器(gcc)和C++编译器(g++)编译产生的函数名不同,导致C++连接找不到C语言的函数,所以C++程序中直接C函数是不能直接实现的,需要通过某种处理

下面通过一个例子简单说明C++和C编译原理的不同之处

add.c

#include "add.h"

int add(int m, int n)
{
    return m + n;
}

add.h

#ifndef ADD_H__
#define ADD_H__
int add(int m, int);
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

上面源码用gcc编译

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m1qdyXwu-1678875989098)(C:/Users/100011704/Desktop/md%E6%96%87%E6%A1%A3/images/12.png)]

同样源码使用g++编译

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yGss3thl-1678875989098)(C:/Users/100011704/Desktop/md%E6%96%87%E6%A1%A3/images/23.png)]

解决:

extern "C"的主要作用就是为了实现c++代码能够调用其他c语言代码。加上extern "C"后,这部分代码编译器按c语言的方式进行编译和链接,而不是按c++的方式。

我们只要在c的.h文件下进行如下操作,再在c++文件中调用c的头文件,就可以链接成功。

c文件的.h文件代码如下

add.c

#include "add.h"

int add(int m, int n)
{
    return m + n;
}

add.h

#ifndef ADD_H__
#define ADD_H__

#ifdef __cpluscplus
extern "C"{
#endif

int add(int m, int);


#ifdef __cpluscplus
};
#endif

#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

(extern"C"是C++特有的,所以上面加上宏__cpluscplus判断当使用C++编译时才加上extern "C"关键字)

下面写个简单的c++程序去调用c函数

main.cpp

#include <iostream>
#include "add.h"

int main()
{
    std::cout << add(1,2)<<std::endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

编译:g++ main.cpp add.c -o main

运行

到这里已经完成C++去调用C程序函数

C程序调用C++函数

场景:基本我们linux底层开发都是基于C语言开发,而上层应用基于C++开发,现在我们有C程序需要调用C++中的功能函数,该如何实现呢?

假设我们有一个C++类 Robot,在文件 robot.hrobot.cpp 中定义。Robot 类中有个成员函数 sayHi() 我们想在C程序中调用这个函数。

C++部分代码如下:

robot.h

#pragma once

#include <string>

class Robot
{
public:
    Robot(std::string name) : name_(name) {}

    void sayHi();

private:
    std::string name_;
};


------------------------
robot.cpp

#include <iostream>

#include "robot.h"

void Robot::sayHi()
{
    std::cout << "Hi, I am " << name_ << "!\n";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

我们编译生成g++的动态库

g++ -fpic -shared robot.cpp -o librobot.so

nm librobto.so     #nm命令可以查看动态库的符号表
  • 1
  • 2
  • 3

此时完成c++动态库的生成,如果我们现在有C程序想要根据C++动态库和头文件直接使用是不行的(因为动态库是基于C++编译的不能修改编译方式的,C编译器并不能识别)

解决方案:
直接调用不可行,可以定义一个中间类完成转换

如下定义一个中间类Middle的接口,定义在Middele.h和Middle.cpp,该接口会定义一个C函数 Robot_sayHi(const char *name), 这个函数会创建一个类 Robot 的实例,并调用 Robot 的成员函数 sayHi()。

Middele.h
#pragma once

#ifdef __cplusplus
extern "C" {
#endif

void Robot_sayHi(const char *name);

#ifdef __cplusplus
}
#endif

-----------------------------------------
Middle.cpp

#include "Middle.h"
#include "robot.h"

#ifdef __cplusplus
extern "C" {
#endif

// 因为我们将使用C++的编译方式,用g++编译器来编译 robot_c_api.cpp 这个文件,
// 所以在这个文件中我们可以用C++代码去定义函数 void Robot_sayHi(const char *name)(在函数中使用C++的类 Robot),
// 最后我们用 extern "C" 来告诉g++编译器,不要对 Robot_sayHi(const char *name) 函数进行name mangling
// 这样最终生成的动态链接库中,函数 Robot_sayHi(const char *name) 将生成 C 编译器的符号表示。

void Robot_sayHi(const char *name)
{
    Robot robot(name);
    robot.sayHi();
}

#ifdef __cplusplus
}
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

同样用编译C++代码的方式进行编译

g++ -fpic -shared  Middle.cpp -L. -lrobot -o libmiddle.so
  • 1

通过nm libmiddle.so 命令可知动态链接库提供了一个C函数 Robot_sayHi(const char *name),我们可以在C程序中调用它了

下面就可以写一个测试的c文件完成调用了

main.c

-
  • 1

使用C程序的编译方式,用 gccmain.c 进行编译

gcc main.c -L. -lmiddle
  • 1

通过objdump -t main命令可以看到可以看到 gcc 编译出的函数符号和 libmiddle.sog++ 编译器编译出的函数符号一致。这样最终在我们的C程序中可以正确的链接到动态库中的Robot_sayHi(const char *name) 函数。

运行可能出现如下报错:

./main: error while loading shared libraries: libmiddle.so: cannot open shared object file: No such 
  • 1

运行可能找不到动态库的路径导致报错,可以配置/etc/ld.so.conf 或者~/bashrc或者/etc/profile配置文件既可以

可参考如下连接:

https://blog.csdn.net/weixin_42310458/article/details/125180410

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/194899
推荐阅读
相关标签
  

闽ICP备14008679号