C语言无法直接调用C++代码,因为C++存在名称修饰(name mangling)、类、虚函数等C不支持的特性。但可以通过中间层封装的方式,让C语言间接使用C++代码,核心思路是:
- 用
extern "C"
声明一个C兼容的接口层(函数) - 在接口层内部实现对C++代码的调用
- C语言通过调用这个C兼容接口来间接使用C++功能
具体实现步骤
1. 编写C++类(待被C调用的功能)
假设我们有一个C++类MyCppClass
,包含需要被C使用的功能:
// cpp_class.h
class MyCppClass {
private:
int value;
public:
MyCppClass(int v) : value(v) {}
int add(int num) { return value + num; }
void setValue(int v) { value = v; }
};
2. 编写C兼容的接口层(关键)
创建一个中间层,用extern "C"
声明C语言可识别的函数,内部封装对C++类的操作:
// c_wrapper.h(C和C++都能包含的头文件)
#ifndef C_WRAPPER_H
#define C_WRAPPER_H
#ifdef __cplusplus
extern "C" { // 告诉C++编译器,以下函数按C规则编译
#endif
// 用void*封装C++对象(C语言无法识别MyCppClass类型)
typedef void* CppObjHandle;
// 创建C++对象(封装构造函数)
CppObjHandle createCppObj(int initialValue);
// 调用C++对象的方法(封装成员函数)
int callAdd(CppObjHandle handle, int num);
void callSetValue(CppObjHandle handle, int v);
// 销毁C++对象(封装析构函数)
void destroyCppObj(CppObjHandle handle);
#ifdef __cplusplus
}
#endif
#endif
3. 实现接口层(调用C++代码)
// c_wrapper.cpp(必须用C++编译器编译)
#include "c_wrapper.h"
#include "cpp_class.h"
// 实现C兼容接口,内部调用C++类
CppObjHandle createCppObj(int initialValue) {
// 创建C++对象,返回其指针(用void*隐藏类型)
return new MyCppClass(initialValue);
}
int callAdd(CppObjHandle handle, int num) {
// 将void*转换为C++对象指针,调用成员函数
MyCppClass* obj = static_cast<MyCppClass*>(handle);
return obj->add(num);
}
void callSetValue(CppObjHandle handle, int v) {
MyCppClass* obj = static_cast<MyCppClass*>(handle);
obj->setValue(v);
}
void destroyCppObj(CppObjHandle handle) {
MyCppClass* obj = static_cast<MyCppClass*>(handle);
delete obj; // 释放C++对象
}
4. C语言调用接口层
C代码只需包含c_wrapper.h
,调用C兼容接口即可间接使用C++功能:
// main.c(C语言代码)
#include "c_wrapper.h"
#include <stdio.h>
int main() {
// 1. 创建C++对象(通过C接口)
CppObjHandle obj = createCppObj(10);
// 2. 调用C++对象的方法
printf("10 + 5 = %d\n", callAdd(obj, 5)); // 输出15
callSetValue(obj, 20);
printf("20 + 5 = %d\n", callAdd(obj, 5)); // 输出25
// 3. 销毁C++对象
destroyCppObj(obj);
return 0;
}
5. 编译运行
需要分别编译C和C++代码,再链接到一起(以GCC为例):
# 编译C++接口层(生成目标文件)
g++ -c c_wrapper.cpp -o c_wrapper.o
# 编译C代码(生成目标文件)
gcc -c main.c -o main.o
# 链接所有目标文件(用g++链接,确保C++运行时被包含)
g++ main.o c_wrapper.o -o c_call_cpp
# 运行程序
./c_call_cpp
核心原理
- 名称修饰兼容:
extern "C"
让接口函数按C语言规则编译(不进行名称修饰),确保C语言能识别函数名。 - 类型隐藏:用
void*
封装C++对象指针,避免C语言直接接触C++的类类型(C不支持类)。 - 功能转发:接口层函数内部完成
void*
到C++对象指针的转换,并调用对应的成员函数,实现功能转发。
注意事项
- C语言无法直接访问C++的类成员、模板、虚函数等特性,必须通过接口层函数间接操作。
- 内存管理必须通过接口层的
create
/destroy
函数完成,避免C和C++混用malloc
/free
与new
/delete
导致内存泄漏。 - 链接时需用C++编译器(如
g++
),确保C++标准库被正确链接。