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 #endifc.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