1. 问题成因
C与C++调用问题原因主要在于C编译器和C++编译器的不同。C是过程式语言,C编译器编译后,函数在符号库中就是函数名,没有其他任何附加信息。而C++是对象式语言,支持函数重载,C++编译器编译后,在符号库中的名字包含了函数名、函数参数类型和函数返回类型等。因此,当两者混合编译时,会相互找不到对象。
2. 反汇编证明
我们通过反汇编来证明上述问题,请看下面的例子:
#include "stdio.h" int funP(int a) { return 0; } int main() { int a = 0,ret; ret = funP(a); return 0; }
对这一段小程序test.c,我们分别使用gcc和g++两种工具进行编译:
gcc test.c -o testc g++ test.c -o test+
对可执行程序进行反汇编
编辑
可以看到gcc编译的testc反汇编出来函数名为就是funP,而g++编译的test+反汇编出来函数名为_Z4funPi,带上了返回类型和函数参数类型。两者的不一致导致C与C++相互调用无法找到对应函数。
在C和C++混合编程中,常见的是C++调用C,这种是比较简单的,一个extern “C”{}即可解决问题。而C调用C++则是使用相对较少的,我们通过一个实例来说明C如何调用C++类成员函数。
3.C++程序
3.1 add.h
#ifndef ADD_H #define ADD_H class addCode { public: addCode(); ~addCode(); void setNum(int a); int getNum(); private: int m_a; }; #endif
3.2 add.cpp
#include <stdio.h> #include "add.h" addCode::addCode() { m_a = 0; } addCode::~addCode() { } void addCode::setNum(int a) { a++; printf("c++ a = %d\n",a); m_a = a; } int addCode::getNum() { return m_a; }
4. 中间封装程序
通过中间封装程序,实现C与C++之间的转换,中间封装头文件提供给C和C++程序来调用。
4.1 myadd.h
extern “C”{ } 告诉C++编译器以C的规则来链接函数,以便C编译器之后在对象文件中找到正确的符号。#ifdef _ucplusplus 是因为C编译器不知道关键字extern。
#ifndef MYADD_H #define MYADD_H #ifdef __cplusplus extern "C"{ #endif typedef struct addCode addCode; addCode* newAddCode(); void interSetNum(addCode *v,int a); int interGetNum(addCode *v); #ifdef __cplusplus } #endif #endif
4.2 myadd.cpp
#include "add.h" #include "myadd.h" extern "C"{ addCode* newAddCode(){ return new addCode(); } void interSetNum(addCode *v,int a) { v->setNum(a); } int interGetNum(addCode *v) { return v->getNum(); } }
5. C主程序 main.c
#include <stdio.h> #include "myadd.h" int main(void) { int a = 1; struct addCode* v = newAddCode(); //传递参数给C++ interSetNum(v,a); printf("main a = %d\n",a); //获取C++中参数值 a = interGetNum(v); printf("main get a = %d\n",a); return 0; }
6. 编译执行
为了体验过程,我们不再编写makefile,依次使用如下命令行进行编译:
g++ -c add.cpp -o add.o g++ -c myadd.cpp -o myadd.o gcc -c main.c -o main.o g++ add.o myadd.o main.o -o test
编译完成,执行程序,结果如下:
编辑
通过上述例子,我们实现了C对C++的调用,大家可以参考。