IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 在C++编译器下,将代码按照C语言编译 -> 正文阅读

[C++知识库]在C++编译器下,将代码按照C语言编译

一、定义-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版本之后的编译器才支持。同时前者只能针对整个头文件,而后者可以针对文件中的部分代码。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-11-20 18:12:41  更:2021-11-20 18:12:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:28:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码