发布于 

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

最近在看人工智能相关的知识,无意中发现了一个巨牛的 人工智能教程,分享一下给大家。

教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!觉得太牛了,所以分享给大家。点 这里 可以直接看教程。

简介

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

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

下面看具体的例子.

C++ 调用 C

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

CFile.h

1
2
3
4
5
6
7
8
#ifndef CFile_h
#define CFile_h

#include <stdio.h>

extern void start_c(int cmd);

#endif /* CFile_h */

CFile.c

1
2
3
4
5
6
#include "CFile.h"

void start_c(int cmd) {

printf("start_c by cmd: %i\n", cmd);
}

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

main.cpp

1
2
3
4
5
6
7
8
9
10
extern "C" {
#include "CFile.h"
}

int main(int argc, const char * argv[]) {

start_c(1);

return 0;
}

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

1
2
3
extern "C" {
#include "CFile.h"
}

如果直接导入, 如:

1
2
3
4
5
6
7
8
#include "CFile.h"

int main(int argc, const char * argv[]) {

start_c(1);

return 0;
}

编译报错:

1
2
3
4
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

1
2
3
4
5
6
#ifndef CPPFile_hpp
#define CPPFile_hpp

void start_cpp(int cmd);

#endif /* CPPFile_hpp */

CPPFile.cpp

1
2
3
4
5
6
7
8
9
#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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#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

1
2
3
4
5
6
7
8
9
10
#include "CFile.h"

//声明函数
extern void adapter_start_cpp(int cmd);

void start_c(int cmd) {

//调用 c++ 代码
adapter_start_cpp(5);
}

C 调用 C++ 类中的方法

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

CPPClassFile.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#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

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
#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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#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” 表示的是一种链接约定.

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++ 编译器默认定义的.

类似如下的代码:

1
2
3
4
5
6
7
8
9
#ifdef __cplusplus
extern "C"{
#endif

void fun(int, size_t);

#ifdef __cplusplus
}
#endif

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

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

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


本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

veryitman