赞
踩
Typist : Akame Qixisi / Excel Bloonow
extern
是我在学习C/C++的过程中存在疑惑较多的一个关键词,本文章简要记录了extern的基本用法;并较为详细的记录了在C++代码中extern "C"
的作用。
extern
是计算机语言C/C++中一个关键字,可用于变量或函数前;对于使用者,它声明一个外部符号,以表示该变量或函数是定义在别的文件中的外部符号;对于创建者,它用在头文件中的一个声明之前或一个定义之前,显式说明该符号可以被其他文件使用(有些形式具有默认的外部链接,可以不用extern显式说明)。另外,extern也可以用来进行链接指定。
在编译时,提示编译器遇到此变量或函数时,知道这个标识符在其他文件中定义过,可以编译通过。在链接时,提示链接器在其他文件中寻找其定义。
需要注意的是,根据前述指针与数组的区别,在一个文件中使用int a[8]
定义一个数组,而在另一个文件中使用extern int* a
去声明这个外部符号,编译可以通过,但它们的链接是不成功的。因此说明,在一个文件中定义的外部符号和在另一文件中声明的外部符号,必须是严格类型相同的。
变量、函数(free函数默认有外部链接):
// a.c // 定义外部符号,以供其他文件使用,注意参考《大规模C++程序设计》查阅默认具有外部链接的形式
int x = 10;
int add (int a, int b) { return a + b; }
// b.c // 声明外部符号,以便使用其他文件定义的符号,可直接使用
extern int x;
extern int add(int, int);
现代编译器一般采用按文件编译(其实是编译单元)编译的方式,因此在编译时,各个编译单元中定义的全局变量是互相不透明的。也就是,在编译时,全局变量的可见域限制在文件内部。到了链接阶段,要将各个编译单元中的符号链接到一个程序中,因而,定义于某编译单元的全局变量,在链接完成后,它的可见范围被扩大到了整个程序,很容易推导出,一个文件中定义的全局变量,可以在整个程序的任何地方都使用。在链接时,如果在不同的编译单元中存在重复的全局定义,则会链接失败。
首先明确,extern "C"
是 C++ 中的写法( .h 头文件,.cpp 实现文件),C语言中并没有该写法。
总括:extern “C” 是 C++ 语言为了兼容 C 程序、使用 C 语言库,实现C++与C以及其他语言混合编程所引入的。为了更好的支持原来的C代码和已经写好的C语言库,需要在C++中尽可能的支持C,而 extern “C” 就是其中的一个策略。任何语言中的任何语法特性的诞生都不是随意而为的,来源于真实世界的需求驱动。
在C++环境下使用C函数的时候,常常会出现编译器无法找到目标模块中的C函数定义,从而导数链接失败。原因如下:
对于一个函数 int add(int a, int b)。由于C++支持重载,编译器为了解决函数的重载问题,会将函数名和参数类型一起加到编译后的代码中,合起来生成一个中间的函数名称,如 _add_int_int 这样的符号;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名,如 _add 这样的符号。不同的编译器可能生成的名字不同,但是都采用了相同的机制,生成的新名字称为”mangled name“。由于在C++代码中编译的符号和C代码中编译的符号不同,自然链接不成功。
同样地,本质上,编译器在C++中的类成员变量等时,与函数的处理相似,也为类中的变量取了一个独一无二的名字,这个名字与代码中的名字不同。
extern “C” 的主要作用就是为了能够正确实现C++代码调用其他C语言代码,加上extern "C"后,会指示编译器这部分(C++)代码按C语言(而不是C++)的方式进行编译,即函数编译成的符号不带参数类型信息,从而C++代码编译后在目标文件中的符号,就和C代码编译成的符号一样,自然可以链接成功。一般来说有三种使用方式:
// myheader.h C++头文件
extern "C" int add(int, int); // 对于单一函数使用
extern "C" { // 可以是复合语句,相当于复合语句中的声明都使用了 extern "C"
int sub(int, int);
double mul(int, int);
}
extern "C" { // 可以包含头文件,相当于头文件中的声明都使用了 extern "C"
#include <cmath>
}
由于对头文件来说,无法靠后缀名等形式区分是C语言的头文件还是C++的头文件,所以常使用一个C++才有的宏来判断是否是C++文件,如下:
// myheader.h
#ifdef __cplusplus // C++文件(编译器)才有的宏定义,如果定义了,就说明这是C++代码
extern "C" {
#endif
int sub(int a, int b);
#ifdef __cplusplus
}
#endif
常见的使用场合为:C++代码调用C语言代码;在C++的头文件中使用;在多人协同开发时,可能有人比较擅长C语言,有人擅长C++等。如在C++中使用C语言函数或C语言的库函数:
// c_sample.h // C头文件
#ifndef C_SAMPLE_H
#define C_SAMPLE_H
extern int add(int x, int y); // 自由free函数默认居然外部链接,可以不用extern
#endif
// c_sample.c // C实现文件
#include "c_sample.h"
int add(int x, int y) { return x + y; }
// tool_model.cpp // C++实现文件,也可以为C++头文件或其他C++文件
extern "C" {
#include "c_sample.h" // 可以采用其他方式,推荐使用头文件包含
}
// use the c function somewhere.
int result = add(lvalue, rvalue);
同理,如果要从C++语言代码中导出函数,一共C语言程序使用,也需要在C++源文件要导出的函数前使用 extern “C” 来修饰,以按C语言编译风格编译,让C语言程序可以调用。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。