C/C++: 如何相互调用

2018-02-12 10:45:29来源:http://www.veryitman.com/2018/02/12/C-C-如何相互调用/作者:veryitman人点击

分享

C++ 中调用 C 比较简单. 但是 C 调用 C++ 稍微复杂一些.


C 调用 C++ 分为可以调用 C++ 类中的函数和普通 cpp 中的函数. 无论是哪种函数, 我们都可以使用封装了 C++ 的文件作为适配供给 C 来使用.


下面看具体的例子.


C++ 调用 C

首先创建 CFile.h 和 CFile.c 文件.


CFile.h


#ifndef CFile_h
#define CFile_h
#include <stdio.h>
extern void start_c(int cmd);
#endif /* CFile_h */

CFile.c


#include "CFile.h"
void start_c(int cmd) {
printf("start_c by cmd: %i/n", cmd);
}

在 C++ 文件中调用 C 代码, 示例如下:


main.cpp


extern "C" {
#include "CFile.h"
}
int main(int argc, const char * argv[]) {
start_c(1);
return 0;
}

这里可以看到导入 C 文件的方式:


extern "C" {
#include "CFile.h"
}

如果直接导入, 如:


#include "CFile.h"
int main(int argc, const char * argv[]) {
start_c(1);
return 0;
}

编译报错:


Undefined symbols for architecture x86_64:
"start_c(int)", referenced from:
_main in main.o
ld: symbol(s) not found for architecture x86_64


这里的extern "C"
告诉编译器, 要按照 C 的链接约定,而不是 C++ 的链接约定.



C 编译器不支持extern "C"
.


C 调用普通 C++ 的函数

C 调用 C++ 有点曲折.


CPPFile.hpp
#ifndef CPPFile_hpp
#define CPPFile_hpp
void start_cpp(int cmd);
#endif /* CPPFile_hpp */
CPPFile.cpp
#include "CPPFile.hpp"
#include <iostream>
using namespace std;
void start_cpp(int cmd) {
cout << "start_cpp by cmd: " << cmd << endl;
}

这里需要写一个中间的 cpp(CPPAdapter.cpp)文件, 提供方法给 C 来使用.



注意: 这里没有CPPAdapter.hpp
头文件.


CPPAdapter.cpp
#include "CPPFile.hpp"
#ifdef __cplusplus
extern "C" {
#endif
void adapter_start_cpp(int cmd) {
//调用 CPPFile 中的方法
start_cpp(cmd);
}
#ifdef __cplusplus
}
#endif

然后在 C 中调用 C++ 的代码:


CFile.c


#include "CFile.h"
//声明函数
extern void adapter_start_cpp(int cmd);
void start_c(int cmd) {
//调用 c++ 代码
adapter_start_cpp(5);
}
C 调用 C++ 类中的方法

和上面例子的原理一样的.


CPPClassFile.hpp
#ifndef CPPClassFile_hpp
#define CPPClassFile_hpp
#include <iostream>
#include <string>
using namespace std;
class Person {
private:
string name;
public:
Person();
~Person();
int setName(string name);
};
#endif /* CPPClassFile_hpp */
CPPClassFile.cpp
#include "CPPClassFile.hpp"
Person::Person() {
cout << "Person()" << endl;
}
Person::~Person() {
cout << "~Person()" << endl;
}
int Person::setName(string name) {
this->name = name;
cout << "Set name: " << name << endl;
return 0;
}
CPPAdapter.cpp
#include "CPPFile.hpp"
#include "CPPClassFile.hpp"
#ifdef __cplusplus
extern "C" {
#endif
void adapter_start_cpp(int cmd) {
//调用 CPPFile 中的方法
start_cpp(cmd);
}
int adapter_set_name(const char *cName) {
Person *person = new Person();
int ret = person->setName(cName);
delete person;
return ret;
}
#ifdef __cplusplus
}
#endif

在 C 中可以调用了, 如下代码:


CFile.c


#include "CFile.h"
///声明 CPPFile 中的方法
extern void adapter_start_cpp(int cmd);
///声明 CPPClassFile 中的方法
extern int adapter_set_name(const char *cName);
void start_c(int cmd) {
printf("start_c by cmd: %i/n", cmd);
//调用 CPPFile 中的方法
adapter_start_cpp(5);
//调用 CPPClassFile 中的方法
adapter_set_name("www.veryitman.com");
}
extern “C”


extern "C"
中的 “C” 并不表示 C 语言,”C” 表示的是一种链接约定.


C 和 C++ 语言之间的密切关系而在它们之间更多的应用而已.



extern "C"
指令描述的是一种链接约定,它并不影响调用函数的定义,即使做了该声明,对函数类型的检查和参数转换仍要遵循 C++ 的标准,而不是 C 的标准.


不同的编程语言(编译型)链接特性是不同的,这也决定了它们编译后的链接符号的不同.



如函数void function(int d)
,C 语言会把它编译成类似_function
这样的符号,C 链接器只要找到该函数符号就可以链接成功.



C++ 会把这个函数编译成类似_function_int
或_xxx_functionIntxxx
这样的符号,即在符号上增加了类型信息,这也解释了为什么 C++ 可以实现函数重载了.



那么,对于用 C 编译器编译成的库,用 C++ 直接链接势必会出现不能识别符号的问题,用extern "C"
就可以解决, 正如上面的例子.



简单来说,extern "C"
的作用就是让编译器知道要以 C 语言的方式编译和链接函数.


__cplusplus 宏


__cplusplus
宏是 C++ 编译器默认定义的.


类似如下的代码:


#ifdef __cplusplus
extern "C"{
#endif
void fun(int, size_t);
#ifdef __cplusplus
}
#endif

在 C++ 中, 编译器将 fun 按照 C 的链接约定来编译, 而如果是 C 编译器, 直接按照 C 的链接约定来编译即可.



__cplusplus
是在 C++ 编译器中默认定义的,C语言不支持extern “C”
.


上面的代码很实用, 也是一种编程技巧.


最新文章

123

最新摄影

闪念基因

微信扫一扫

第七城市微信公众平台