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"
.
上面的代码很实用, 也是一种编程技巧.