被extern "C"修饰的代码会按照C语言的方式去编译
我们都知道,C语言不支持函数重载,只有C++才支持函数重载。下方两个函数就会按照C语言进行编译,由于是重载函数,所以会报错
extern "C" void function(int v1, double v2)
{
cout << "int v1, double v2" << endl;
}
extern "C" void function(double v1, int v2)
{
cout << "double v1, int v2" << endl;
}
int main(int argc, char **argv)
{
function(10,10.0);//会报错,因为C语言不支持函数重载
getchar();
return 0;
}
第三方库math.c文件,提供了一个函数,但用的C语言写的
int sum(int v1,int v2)
{
return v1 + v2;
}
my.cpp自己的cpp工程文件,想要调用math.c里的函数为我所用
int sum(int v1,int v2);//声明一下
//C++编译器一看到这个函数,可能就直接name manuling了,改成了sum_i_i
int main()
{
sum(1,1);
getchar();
return 0;
}
//这时候就会出错,因为C语言编译规则和C++编译规则不相同。可能C++看到sum的函数声明,直接把名字改成sum_i_i,而C语言中的名字就是sum,所以C++编译器就会找不到函数,就会报错。
第三方库math.c文件,提供了一个函数,但用的C语言写的
int sum(int v1,int v2)
{
return v1 + v2;
}
my.cpp自己的cpp工程文件,想要调用math.c里的函数为我所用
extern "C" int sum(int v1,int v2);//声明一下
//这里我要告诉C++编译器,这个函数是C语言编译的,你不要乱改名字了。如此,就可以实现C++环境调用C语言里的东西
int main()
{
sum(1,1);//2
getchar();
return 0;
}
函数如果同时有声明(.h文件)和实现(.c),extern “C"修饰只放在函数声明中,即.c文件实现函数时,可以不用再次修饰。如果.cpp文件引用.h中的C语言函数时:如果.h的函数已经被extern “C"修饰,则直接#include””;如果没有被修饰,则必须extern “C” #include""
AAA.h文件
void func_c();//声明时不用extern "C"修饰,那#include时就要用extern "C"修饰
AAA.c文件
void func_c()//实现
{
//函数体
}
AAA.cpp文件
extern "C" #include"AAA.h"//由于AAA.h文件中的函数没有被extern "C" 修饰,所以想要这个函数按照C语言编译,就必须将include转换。
int main()
{
func_c();
getchar();
return 0;
}
AAA.h文件
extern "C" void func_c();//声明时用extern "C"修饰,那#include时就不用extern "C"修饰
AAA.c文件
void func_c()//实现
{
//函数体
}
AAA.cpp文件
#include"AAA.h"//由于AAA.h文件中的函数有被extern "C" 修饰,因此这里直接include就可以
int main()
{
func_c();
getchar();
return 0;
}
有函数声明和实现时,想要某个函数采用C语言编译,最好只在函数声明前采用extern “C” 修饰,在函数实现前不做任何操作
由于整个项目C++需要调用第三方库(第三方库是用C语言写的),所以用extern “C” 修饰一下这个第三方库里的函数,即可被C++调用。但,我这个项目里如果有.c文件也需要调用这个第三方库,可以直接调用吗?答案是不能的,因为这个第三方库函数已经被extern “C” 修饰了,而C语言是不认识extern “C” 的。
我希望这个第三方库更加的灵活,即C++调用函数时自动加上extern “C” 修饰;C语言调用,extern “C” 自动去掉
#define __cplusplus这个宏被默认的编写在C++文件的最开头,用来确认这个文件是cpp文件。只要你是cpp文件,第一行编译器默认给你写了一句#define __cplusplus。
#ifdef 和 #endif // 组合使用,达到条件编译的目的。下方代码的最终结果就是:如果某个cpp文件调用这段代码,才会编译代码段,c文件调用这段代码,不会编译这段代码段,即只读到了一对注释
#ifdef __cplusplus
//被编译的代码段,如果定义了名为__cplusplus的宏,这段代码段才会被编译,否则会被编译器视为注释
#endif // __cplusplus
AAA.h文件
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void func_c();//声明时用extern "C"修饰,那#include时就不用extern "C"修饰
#ifdef __cplusplus
}
#endif // __cplusplus
AAA.c文件
void func_c()//实现
{
//函数体
}
AAA.cpp文件
#include"AAA.h"//由于AAA.h文件中的函数有被extern "C" 修饰,因此这里直接include就可以
因为是c++环境,所以AAA.h里的代码是这样的:
extern "C" {
void func_c();//因为C++环境在此之前定义了宏 __cplusplus,所以extern "C"代码被编译保存了下来
}
int main()
{
func_c();
getchar();
return 0;
}
other.c文件,也需要调用第三方库AAA.h里的函数
#include"AAA.h"
//因为是c语言环境,所以AAA.h里的代码是这样的:
//extern "C" {
void func_c();
//}
//这是因为.C文件里的最开始没有宏定义 __cplusplus
int main()
{
func_c();
getchar();
return 0;
}
以后凡是用C语言编写的第三方库,最好都要按照下述代码改动.h文件。这样的话,C++和C语言文件都可以调用这个第三方库,不仅不会出错,还会使得代码更加规范
//第三方C语言库
#ifdef __cplusplus
extern "C" {
#endif // __cplusplus
void function1();
void function2();
void function3();
void function4();
void function5();
.....
#ifdef __cplusplus
}
#endif // __cplusplus
我们在开发的时候,当代码量很大时,我们可能会在中间include头文件,下面的组合会解决这个问题
#ifndef BBB
#define BBB
…BBB.h头文件代码
#endif //!BBB
BBB.h文件
//代码
某个.cpp文件
#include
...
可能有很多行代码
...
#include //我在某处又引入了这个头文件,这时候就会报错,因为我重复包含了.h文件
...
BBB.h文件
#ifndef BBB //如果没有定义BBB这个宏
#define BBB //定义BBB这个宏,然后下面代码参与编译
//代码
#endif //!BBB
#include
某个.cpp文件
#include
//#ifndef BBB //如果没有定义BBB这个宏
//#define BBB //定义BBB这个宏,然后下面代码参与编译
代码
//#endif //!BBB
...
可能有很多行代码,然后我忘了前面已经#include<BBB.h>了,我又包含了一遍
...
#include //我在某处又引入了这个头文件,这时候就会报错,因为我重复包含了.h文件
//#ifndef BBB //如果没有定义BBB这个宏,直接判断失败,因为上面已经#define BBB了一次,所以在#endif //!BBB之前的代码都不会编译了
//#define BBB //定义BBB这个宏,然后下面代码参与编译
代码
//#endif //!BBB
...
新建.h头文件时第一行出现的 #pragma once,是为了防止这个.h文件被重复包含,即也是防止cpp文件中多次包含相同的.h文件。#pragma once可以起到和上述相同的作用
#pragma once 会被定义在.h文件的开头,也会起到防止被重复include的错误。但与上面的#ifndef的区别是什么呢?
#pragma once 和 #ifndef + #define + #endif //! 的区别在于,后者什么编译器都支持,而前者则必须保证GCC3.4版本之后的编译器才支持。同时前者只能针对整个头文件,而后者可以针对文件中的部分代码。