06-extern关键字

  • extern用来说明“此变量/函数是在别处定义的,要在此处引用”。
  • 添加extern声明,可以让编译器把寻找定义这件事推迟到链接阶段,而不会在编译阶段直接报“没有定义”的错误。
  • 对extern关键字作用的精确描述:
By using 'extern', you are telling the compiler that whatever follows it will be found (non-static) at link time; don't reserve anything for it in the current pass since it will be encountered later. Functions and variables are treated equally in this regard.

这大概是说:添加extern声明,可以让编译器把“寻找定义”这件事情推迟到链接阶段,而不会在编译阶段报“没有定义”的错误。

  • extern关键字的一般使用场景 “用到未被定义的变量/函数”这种情况一般出现在使用在其它文件中定义的变量/函数时。例如:需要在a.cpp文件中使用在b.cpp中定义的变量/函数。

1. 在a.cpp文件中使用在b.cpp中定义的_函数_

  • 对于“在a.cpp文件中使用在b.cpp中定义的_函数_”这种场景,我们都非常熟悉。一般我们会通过在a.cpp#include <b.h>来解决这个问题。这种方式能够奏效,是因为函数声明默认被隐式的extern关键字所修饰
    在某些情况下,可能b.cpp文件并没有对应的b.h头文件,这时就需要在a.cpp中使用extern关键字声明b.cpp中的函数。

2. 在a.cpp文件中使用在b.cpp中定义的 (全局)变量

对于“在a.cpp文件中使用在b.cpp中定义的 (全局)变量 ”这种场景,我们不常见到。推荐的实现方式如下:

//a.cpp
#include <b.h>

int main(){
    int a= b + 1;
}
//b.h
extern b;
//b.cpp
#include <b.h>
int b = 5;

如果直接在b.h中定义变量b,那么b会在所有include了<b.h>的cpp文件中被定义。这会导致b被多次定义。因此,采用上面的这种实现方式,可以确保b只在b.cpp中被定义一次,其他cpp文件均在链接阶段链接到这个定义。

不要在.h文件中直接定义或声明变量。前者会导致重定义错误,后者会导致歧义(详见此处)。

3. extern “C”

在CPP文件中include C头文件时,需要使用extern "C"。(详见此处) 例如:

#ifdef __cplusplus 
extern "C" { 
#endif 

#include "b.h"

#ifdef __cplusplus 
} 
#endif 

这里b.h是一个C头文件,不是CPP头文件。这样做的原因如下:

假设b.c中定义了函数int b(int,int)。由于C语言没有函数重载这一特性,因此b.c编译成的b.o文件中,这个函数的符号名可能类似于_b。但对于同名的CPP函数,它在.o文件中的符号名会类似于_b_int_int。因此,如果我们直接认为b.h是一个CPP头文件,那么我们无法在链接时正确地找到相应的函数实现。

注:在extern后面添加大括号,其效果是对大括号内的每条语句都添加extern前缀;#include "b.h"语句会在前处理时被替换为b.h中的内容。因此这里相当于对b.h中的每条声明都添加了extern前缀。

comments powered by Disqus