在C++编译器下,将代码按照C语言编译

36 篇文章 4 订阅
5 篇文章 0 订阅

一、定义-defintion

被extern "C"修饰的代码会按照C语言的方式去编译

二、extern "C"使用举例-example

我们都知道,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;
}

三、extern "C"的用武之处在哪?

  • 由于C、C++编译方式的不同,这种机制往往用在C\C++混合开发,往往做项目时,可能会用到第三方库:这个库可能是C语言写的,这时候这种机制就很重要。
第三方库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++编译器就会找不到函数,就会报错。
  • 我还是想要用第三方库里的函数,怎么办呢?这时候就要借助extern “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;
}

四、extern "C"常用特性

1.C++调用第三方库(含.h&.c文件)

函数如果同时有声明(.h文件)和实现(.c),extern “C"修饰只放在函数声明中,即.c文件实现函数时,可以不用再次修饰。如果.cpp文件引用.h中的C语言函数时:如果.h的函数已经被extern “C"修饰,则直接#include””;如果没有被修饰,则必须extern “C” #include""

  • .h头文件中的函数声明没有被extern "C"修饰
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;
}
  • .h头文件中的函数声明有被extern "C"修饰
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” 修饰,在函数实现前不做任何操作

2. C语言也调用第三方库(含.h&.c文件)

由于整个项目C++需要调用第三方库(第三方库是用C语言写的),所以用extern “C” 修饰一下这个第三方库里的函数,即可被C++调用。但,我这个项目里如果有.c文件也需要调用这个第三方库,可以直接调用吗?答案是不能的,因为这个第三方库函数已经被extern “C” 修饰了,而C语言是不认识extern “C” 的。

我希望这个第三方库更加的灵活,即C++调用函数时自动加上extern “C” 修饰;C语言调用,extern “C” 自动去掉

a. #define __cplusplus解释

#define __cplusplus这个宏被默认的编写在C++文件的最开头,用来确认这个文件是cpp文件。只要你是cpp文件,第一行编译器默认给你写了一句#define __cplusplus。

b. #ifdef条件编译

#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. 启示

以后凡是用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

五、不相关学习tips

1.防止某个.h文件被重复include

我们在开发的时候,当代码量很大时,我们可能会在中间include头文件,下面的组合会解决这个问题
#ifndef BBB
#define BBB
…BBB.h头文件代码
#endif //!BBB

  • 错误示例
BBB.h文件
//代码
某个.cpp文件
#include<BBB.h>
...
可能有很多行代码
...

#include<BBB.h>//我在某处又引入了这个头文件,这时候就会报错,因为我重复包含了.h文件
...

  • 正确且规范的编写.h头文件。
BBB.h文件
#ifndef BBB //如果没有定义BBB这个宏
#define BBB //定义BBB这个宏,然后下面代码参与编译

//代码

#endif //!BBB

#include<BBB.h>就相当于把BBB.h文件中的代码全部拷贝一份

某个.cpp文件
#include<BBB.h>
//#ifndef BBB //如果没有定义BBB这个宏
//#define BBB //定义BBB这个宏,然后下面代码参与编译

代码

//#endif //!BBB
...
可能有很多行代码,然后我忘了前面已经#include<BBB.h>了,我又包含了一遍
...

#include<BBB.h>//我在某处又引入了这个头文件,这时候就会报错,因为我重复包含了.h文件
//#ifndef BBB //如果没有定义BBB这个宏,直接判断失败,因为上面已经#define BBB了一次,所以在#endif //!BBB之前的代码都不会编译了
//#define BBB //定义BBB这个宏,然后下面代码参与编译

代码

//#endif //!BBB
...

2.防止某个.h文件被重复include(#pragma)

新建.h头文件时第一行出现的 #pragma once,是为了防止这个.h文件被重复包含,即也是防止cpp文件中多次包含相同的.h文件。#pragma once可以起到和上述相同的作用

#pragma once 会被定义在.h文件的开头,也会起到防止被重复include的错误。但与上面的#ifndef的区别是什么呢?

#pragma once 和 #ifndef + #define + #endif //! 的区别在于,后者什么编译器都支持,而前者则必须保证GCC3.4版本之后的编译器才支持。同时前者只能针对整个头文件,而后者可以针对文件中的部分代码。

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值