extern C配合__cplusplus实现CPP文件和C文件的函数互相调用
C语言 c语言
C和CPP的编译后的区别
先看一下区别,首先看一下编译一个最简单的函数
demo.c
void testFunction(){
return;
}
demo.cpp
extern "C"{
void testFunction(){
return;
}
}
demo.cpp
void testFunction(){
return;
}
分别编译
$ gcc -S demo.c
$ gcc -S demo.cpp
对比一下编译出来的汇编代码
demo.c
加入了 extern “C” 的 demo.cpp
没有 extern “C” 的 demo.cpp
这里从cpp中对函数的重载处理开始说起。
在c++中,为了支持重载机制;
在编译生成的汇编码中,要对函数的名字进行一些处理,加入比如函数的返回类型等等.
而在C中,
只是简单的函数名字而已,不会加入其他的信息.
也就是说:C++和C对产生的函数名字的处理是不一样的
主要区别在于原本的函数名 testFunction 在C文件中保留了原来的名字;
但是在CPP文件中,没有引入extern “C” 编译后名字变更了
testFunction >>>> _Z12testFunctionv
现实中遇到CPP文件中的某一个函数去调用之前用C文件编写和编译好的函数,会出现:
CPP里的函数会根据C++的规则给函数名字加上前缀后缀之后,使用 _Z12testFunctionv 去查找函数的实现
就会出现查找不到函数名的链接异常, 直接就提示了函数未实现
我们需要告诉编译器查找函数实现时,不要使用C++的规则查找,而是使用C语言的规则
C和C++对函数的处理方式是不同的。
extern”C”是使C++能够调用C语言实现的库文件的一个手段
C++之父在设计C++之时
考虑到当时已经存在了大量的C代码 为了支持原来的C代码和已经写好C库
需要在C++中尽可能的支持C,而extern “C”就是其中的一个策略。
最规范的做法是头文件写成
#ifdef __cplusplus
extern "C" {
#endif
void testFunction();
#ifdef __cplusplus
}
#endif
__cplusplus是cpp中的自定义宏
那么定义了这个宏的话表示这是一段cpp的代码;
这样可以判断到,原本C语言实现的函数被链入CPP文件中了,该告诉编译器不要因此使用CPP的规则来处理我,而是使用C的规则
如果没有宏定义 __cplusplus , 说明我仍然是在C文件里面被调用,不需要特殊照顾。
extern”C”是为了CPP文件能够使用到C文件, 在C文件中使用extern”C”是无法通过编译的
会出现 error: expected identifier or ‘(’ before string constant
只能在CPP文件和H文件中调用
参考 http://blog.csdn.net/thanklife/article/details/7362893
C文件和CPP文件中的函数互相调用
// function.cpp
#include <stdio.h>
extern "C" void PrintHello();
void PrintHello(){
printf("I am %s\n", __FILE__);
}
// main.c
#include <stdio.h>
extern void PrintHello();
int main(){
printf("I am %s\n", __FILE__);
PrintHello();
return 0;
}
$ gcc g++ -c function.cpp
$ gcc -c main.c
$ gcc main.o function.o -lstdc++ -o test
$ ./test
I am main.c
I am function.cpp
或者加入头文件的写法
// function.h
#ifndef _FUNCTION_H_
#define _FUNCTION_H_
#ifdef __cplusplus
extern "C" {
#endif
void PrintHello();
#ifdef __cplusplus
}
#endif
#endif
// function.cpp
#include <stdio.h>
#include "function.h"
void PrintHello(){
printf("I am %s\n", __FILE__);
}
// main.c
#include <stdio.h>
#include "function.h"
int main(){
printf("I am %s\n", __FILE__);
PrintHello();
return 0;
}
使用头文件的写法,把文件的后缀互换结果不变;
在main.c中,可以把头文件的引用去掉,但是如果是main.cpp的话,把头文件去掉就编译错误;
这是因为c和c++有个比较大的区别
编译C语言时 编译器会去猜测且主动搜索当前目录或者系统环境变量下的目录的文件 并引入;
编译C++时 编译器严格要求函数声明后使用 无声明直接报错 未定义;
还有一种方法就是把文件编译成库,再调用就即可;
这样就可以在链接生成可执行文件的环节中 省去指定标准C++编译器-lstdc++
$ gcc -c function.c -o function.o
$ ar rcs libfunction.a function.o
$ g++ main.cpp -L./ -lfunction -o test
$ chmod 777 test
$ ./test
I am main.c
I am function.cpp