extern
是啥
简单来说,extern
就是 C/C++ 里一个关键字,用来声明变量或函数,告诉编译器 “这个东西存在,定义在别的地方”,它的核心作用就是声明但不定义,避免重复定义错误。
基本使用场景
1)共享全局变量
假设我们有两个文件,File1.cpp
和 File2.cpp
。
在 File1.cpp
里,我们定义了一个全局变量:
// File1.cpp
int globalVar = 10; // 定义全局变量
然后在 File2.cpp
里,我们想用这个变量,就需要用 extern
声明它:
// File2.cpp
extern int globalVar; // 声明
void func() {
cout << globalVar; // 使用声明过的全局变量
}
这样,File2.cpp
就能用 File1.cpp
里的 globalVar
变量啦。
2)声明外部函数
假设我们有一个头文件 utils.h
,和两个源文件 utils.cpp
和 main.cpp
。
在 utils.h
中,我们可以声明一个函数:
// utils.h
extern void printMsg(); // 声明函数(其实这里的 extern 可以省略,因为函数默认是 extern 类型)
在 utils.cpp
中定义这个函数:
// utils.cpp
#include <iostream>
void printMsg() {
cout << "Hello from utils!" << endl;
}
在 main.cpp
中,我们就可以调用这个函数啦:
// main.cpp
#include "utils.h"
int main() {
printMsg(); // 调用其他文件定义的函数
return 0;
}
3)头文件中的最佳实践
在头文件中用 extern
声明全局变量,在对应的源文件中定义。
举个例子:
// config.h
extern int MAX_SIZE; // 声明变量
// config.cpp
int MAX_SIZE = 100; // 定义变量(这里才会分配内存)
这样做的好处是避免直接在头文件中定义变量,防止多个文件包含头文件时导致链接错误。
extern "C"
:C++ 调用 C 库
C++ 编译器有个小癖好,它会对函数名进行修饰,就像给函数名戴上不同的帽子,以便区分它们的功能和参数。这个过程通常是这样的:假设你写了一个函数 void myFunction(int)
,C++ 编译器可能会把它变成 _Z11myFunctioni
这样的名字(不同的编译器可能有不同的修饰规则,就像是不同的帽子款式)。但 C 编译器没有这个癖好,它会把函数名原封不动地保留。
现在,如果你有个用 C 语言编写的库文件,里面有个函数 void cFunction()
,C 编译器处理后,这个函数名就是简单的 cFunction
。但如果你在 C++ 代码中直接写 cFunction()
,C++ 编译器会按照自己的规则给它加个 “帽子”,变成 _Z10cFunctionv
这样的名字。到了链接阶段,链接器会找不到原来的 cFunction
,因为它只会看到 _Z10cFunctionv
,从而报错。
这时候,extern "C"
就像是一把 “钥匙”,它告诉 C++ 编译器:“这个函数是 C 风格的,别给它加奇怪的帽子啦!” 举个例子:
/*
__cplusplus宏解释:
当使用 C++ 编译器时,__cplusplus 宏被预定义,extern "C" 生效,确保函数按 C 规则编译。
当使用 C 编译器时,忽略 extern "C"(因其不支持该语法),直接声明函数,避免语法错误。
*/
#ifdef __cplusplus
extern "C" { // 使告诉编译器按 C 方式处理
#endif
void c_function(); // 声明 C 语言函数
#ifdef __cplusplus
}
#endif
这样就能确保 C++ 编译器正确链接 C 语言函数啦。
注意事项
extern
变量必须在使用前声明,不然编译器会不认识这个变量。- 变量只能定义一次,但可以声明多次。定义是分配内存的操作,而声明只是告诉编译器变量存在。
- 不要初始化
extern
变量,比如extern int a = 5;
这样写是错误的,因为extern
变量的初始化应该在定义它的文件中进行。
总之,掌握 extern
的用法,可以让代码在模块化开发中实现高效的协作,特别是在跨文件共享变量和函数时非常有用。