浅析extern关键字

C++中extern关键字的使用

文章目录

前言

​ C++ 是一种支持多范式的编程语言,它既可以实现面向对象的编程,也可以实现泛型编程和函数式编程。C++ 还具有与C语言的兼容性,可以方便地调用C语言的库函数和模块。但是,C++ 和C语言在编译和链接的过程中有一些区别,这就需要我们使用 extern 关键字来解决一些问题。本文将介绍 extern 关键字的作用和用法,以及如何在 C++ 中调用C函数和在C中调用 C++ 函数的方法和注意事项。


正文

1. C++与C编译区别

在C++中常在头文件见到extern "C"修饰函数,那有什么作用呢? 是用于C++链接在C语言模块中定义的函数。

C++虽然兼容C,但C++文件中函数编译后生成的符号与C语言生成的不同。因为C++支持函数重载,C++函数编译后生成的符号带有函数参数类型的信息,而C则没有。

例如int add(int a, int b)函数经过C++编译器生成.o文件后,add会变成形如add_int_int之类的, 而C的话则会是形如_add, 就是说:相同的函数,在C和C++中, 编译后生成的符号不同。

这就导致一个问题:如果C++中使用C语言实现的函数,在编译链接的时候,会出错,提示找不到对应的符号。此时extern "C"就起作用了:告诉链接器去寻找_add这类的C语言符号,而不是经过C++修饰的符号。

2. C++调用C函数

C++调用C函数的例子: 引用C的头文件时,需要加extern "C"

cpp 复制代码
//add.h
#ifndef ADD_H
#define ADD_H
int add(int x,int y);
#endif

//add.c
#include "add.h"

int add(int x,int y) {
    return x+y;
}

//add.cpp
#include <iostream>
#include "add.h"
using namespace std;
int main() {
    add(2,3);
    return 0;
}

编译:

sh 复制代码
//创建 add.o 文件
gcc -c add.c

链接:

sh 复制代码
g++ add.cpp add.o -o main

没有添加extern "C" 报错:

c 复制代码
> g++ add.cpp add.o -o main                                   
add.o:在函数'main'中:
add.cpp:(.text+0x0): `main'被多次定义
/tmp/ccH65yQF.o:add.cpp:(.text+0x0):第一次在此定义
/tmp/ccH65yQF.o:在函数'main'中:
add.cpp:(.text+0xf):对'add(int, int)'未定义的引用
add.o:在函数'main'中:
add.cpp:(.text+0xf):对'add(int, int)'未定义的引用
collect2: error: ld returned 1 exit status

添加extern "C"后:

cpp 复制代码
add.cpp
#include <iostream>
using namespace std;
extern "C" 
{
    #include "add.h"
}
int main() {
    std::cout << add(2, 3) << std::endl;
    return 0;
}

编译的时候一定要注意,先通过gcc生成中间文件add.o。

sh 复制代码
gcc -c add.c 

然后编译:

sh 复制代码
g++ add.cpp add.o -o main

通常为了C代码能够通用,即既能被C调用,又能被C++调用,头文件通常会有如下写法:

c 复制代码
#ifdef __cplusplus
extern "C"
{
#endif
int add(int x,int y);
#ifdef __cplusplus
}
#endif

即在C++调用该接口时,会以C接口的方式调用。这种方式使得C++者不需要额外的extern C,而标准库头文件通常也是类似的做法,否则你为何不需要extern C就可以直接使用 stdio.h 中的C函数呢?

3. C中调用C++函数

extern "C"在C中是语法错误,需要放在C++头文件中。

c 复制代码
// add.h
#ifndef ADD_H
#define ADD_H
extern "C" 
{
    int add(int x,int y);
}
#endif

// add.cpp
#include "add.h"

int add(int x,int y) {
    return x+y;
}

// add.c
extern int add(int x,int y);
int main() {
    add(2,3);
    return 0;
}

编译:

sh 复制代码
g++ -c add.cpp

链接:

sh 复制代码
gcc add.c add.o -o main

综上,总结出使用方法,在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern "C"声明,在.c文件中包含了extern "C"时会出现编译语法错误。所以使用extern "C"全部都放在于cpp程序相关文件或其头文件中。

总结出如下形式:

(1)C++调用C函数:

c++ 复制代码
//xx.h
extern int add(...)

//xx.c
int add(){
    
}

//xx.cpp
extern "C" {
    #include "xx.h"
}

(2)C调用C++函数

c 复制代码
//xx.h
extern "C"{
    int add();
}
//xx.cpp
int add(){
    
}
//xx.c
extern int add();

不过与C++调用C接口不同,C++确实是能够调用编译好的C函数,而这里C调用C++,不过是把C++代码当成C代码编译后调用而已。也就是说,C并不能直接调用C++库函数。


总结

​ 本文介绍了extern关键字的作用和用法,以及如何在C++中调用C函数和在C中调用C++函数的方法和注意事项。extern关键字可以帮助我们解决C++和C语言在编译和链接时的符号不匹配的问题,使得我们可以方便地使用C语言的库函数和模块。但是,我们也要注意一些细节,比如++在C语言中不能使用extern "C",在C++中要使用extern "C"包含C的头文件,以及在头文件中要使用条件编译来区分C和C++的情况++。希望本文对你有所帮助,谢谢!

解决C++和C语言在编译和链接时的符号不匹配的问题 ,使得我们可以方便地使用C语言的库函数和模块。但是,我们也要注意一些细节,比如++在C语言中不能使用extern "C",在C++中要使用extern "C"包含C的头文件,以及在头文件中要使用条件编译来区分C和C++的情况++。希望本文对你有所帮助,谢谢!

相关推荐
wo_jack3 分钟前
C++ 入门笔记(十六):第16课 vector 模拟实现十问十答 —— 从三指针到迭代器失效
c++·后端·面试·stl·vector
D e re k ZH33 分钟前
C++ 标准库算法全景指南
数据结构·c++·算法
冻柠檬飞冰走茶1 小时前
字母移动游戏题解:贪心求最长可保留连续子串(附 C 实现)
c语言·开发语言·数据结构·算法·动态规划
FRing032 小时前
vue2干货总结7(自定义指令、插槽、单页应用程序、路由的使用(VueRouter))
开发语言·前端·vue.js·vue2
雪落漂泊2 小时前
C++算法 二分查找篇(上)
开发语言·c++·算法
波力海苔夹心脆6753 小时前
C# WPF 资源字典详解:ResourceDictionary、MergedDictionaries 与自定义控件案例
开发语言·c#·wpf·入门教程·resource·资源字典
不会算法的笨小孩3 小时前
异常的使用以及会出现的问题
开发语言·c++·c++11·异常
Hhy_11075 小时前
《C++深度解构06》手撕 string —— 从底层内存管理到深浅拷贝彻底吃透
c语言·开发语言·c++·学习·visual studio
starzy19905 小时前
深入理解Scala函数定义:从方法到函数的类型跨越
开发语言·scala
starzy19906 小时前
深入理解Scala递归函数:从栈溢出到尾调用优化
开发语言·scala