C++extern关键字

it2025-02-15  4

1、extern 修饰变量和函数

用extern修饰一个变量,其实就是告诉编译器,这个变量是要引用外部链接文件中的变量,而不是要定义一个变量。因此编译时不会给extern变量分配内存,因为编译器会给定义分配空间,而不会给声明分配空间。

2、extern "C"

extern “C” 相当于告诉编译器,这部分的变量和函数使用C语言的规则来编译(因为C++开发有时需要调用一些C语言代码)。

可以在C库的头文件中使用extern “C”:

#ifdef __cplusplus extern "C" { #endif // all of your legacy C code here #ifdef __cplusplus } #endif

或者在C++代码文件里使用extern “C”

extern "C" { #include "cExample.h" }

为什么要使用extern "C"

C++支持函数重载,编译函数时不仅记录函数名,还需要记录函数参数类型和数量,而C不支持函数重载,因此只需要记录函数名即可。也就是说,如果C++使用一个C语言库的函数时不加extern “C”,那么编译的时候,使用g++编译C++代码文件,使用gcc编译C代码文件,结果对于同一个函数void foo(int a),C++把它编译成_foo_int(只是简单举例,实际不是这样),C把它编译成_foo,结果会导致链接出错,因为C++找不到这个函数。而C++调用C库函数时如果使用extern "C"将其包起来,则C++编译器会按照C语言的规则来编译这个函数(即_foo),就能链接成功。

使用extern "C"的例子

假设有两个C语言的文件:c.h和c.c,前者是头文件,后者是具体的实现代码。还有一个C++程序文件main.cpp,三者内容如下:

main.cpp

#include <cassert> #include "c.h" int main() { assert(f() == 1); }

c.h

#ifndef C_H #define C_H /* This ifdef allows the header to be used from both C and C++ * because C does not know what this extern "C" thing is. */ #ifdef __cplusplus extern "C" { #endif int f(); #ifdef __cplusplus } #endif #endif

c.c

#include "c.h" int f(void) { return 1; }

编译、链接和运行程序的命令如下:

g++ -c -o main.o -std=c++98 main.cpp gcc -c -o c.o -std=c89 c.c g++ -o main.out main.o c.o ./main.out

其中,使用g++编译main.cpp得到目标文件main.o,使用gcc编译c.c得到目标文件c.o,最后使用g++将main.o和c.o链接起来得到可执行文件main.out,最后运行main.out即可。其中,gcc按照C语言规则编译c.c,因此函数 f 被编译成 _f ;g++在编译main.cpp时,发现头文件中有extern “C”,因此按照C语言规则来编译,也把函数 f 编译成 _f。最终g++能够成功链接c.o和main.o。

假如头文件c.h中没有使用extern “C”,那么g++编译main.cpp时会把函数 f 编译成其他模样(叫做mangled,即被打乱了,而不是C语言那样只是简单加下划线),导致main.o和c.o中对同一个函数的表示方法不一致,最终会导致链接出错:

main.cpp:6: undefined reference to `f()'

参考Decompile a g++ generated binary to see what is going on

最新回复(0)