在 C++ 中,extern 关键字主要用于声明变量或函数(告诉编译器"这个变量/函数在别的地方定义了"),或者用于改变 const 变量的默认链接属性。
普通变量和 const 变量在使用 extern 时,最核心的区别在于:默认的链接属性(Linkage)不同。
-
普通全局变量 :默认具有外部链接(External Linkage),可以被其他文件访问。访问的是同一个对象,地址相同
-
const全局变量 :默认具有内部链接(Internal Linkage) ,仅在当前文件可见(相当于默认加了static)。这是 C++ 为了防止头文件中定义const变量导致多重定义冲突而设计的。当然也可以被多个文件同时访问,但访问的是否是同一个对象,地址是否相同,看你怎么定义和声明
这里说防止头文件中定义 const 变量导致多重定义冲突是什么意思?可以看这篇文章:
const 全局变量默认具有内部链接为什么可以防止头文件导致出现多重定义问题
那难道就不怕普通全局变量 在头文件中导致多重定义冲突吗,不怕,头文件里面编译器就不给你定义普通变量,只允许你在头文件里面声明普通变量。
下面通过具体的代码场景来演示这种区别:
场景:在多个源文件中共享全局变量,注意是源文件,没有头文件
假设我们有两个文件:main.cpp 和 other.cpp。
1. 普通变量的 extern 使用
对于普通变量,在定义时不需要 加 extern。在别的文件中使用时,用 extern 声明一下即可。
other.cpp (定义)
cpp
int global_num = 100;
main.cpp (使用)
cpp
#include <iostream>
// 声明:告诉编译器 global_num 在别的文件里定义过
extern int global_num;
int main() {
std::cout << "普通变量: " << global_num << std::endl; // 输出 100
return 0;
}
2. const 变量的 extern 使用
声明 处加extern 是必须的,且定义处也必须加 extern
如果直接在 other.cpp 写 const int const_num = 200;
cpp
int global_num = 100;
const int const_num = 200;
然后在 main.cpp 写 extern const int const_num;
cpp
#include <iostream>
// 声明:告诉编译器 global_num 在别的文件里定义过
extern int global_num;
//声明:告诉编译器 const_num在别的文件里定义过
extern const int const_num;
int main() {
std::cout << "普通变量: " << global_num << std::endl; // 输出 100
std::cout << "const变量: " << const_num << std::endl;
return 0;
}
链接会报错 (undefined reference),注意,vscode编译语法校验不会直接报红,是可以通过的,使用ctrl+alt+b 或者使用g++指令去编译出exe才会报错
其中最重要的报错信息:
cpp
undefined reference to `const_num'
因为 other.cpp 里的 const_num 默认是内部链接,main.cpp 找不到它。
要共享 const 变量,定义时必须显式加上 extern,强制将其改为外部链接。
other.cpp (定义)
cpp
int global_num = 100;
// 必须加 extern,强制将其变为外部链接,否则 main.cpp 找不到
extern const int const_num = 200;
main.cpp (使用)
cpp
#include <iostream>
// 声明:告诉编译器 global_num 在别的文件里定义过
extern int global_num;
//声明:告诉编译器 const_num在别的文件里定义过
extern const int const_num;
int main() {
std::cout << "普通变量: " << global_num << std::endl; // 输出 100
std::cout << "const变量: " << const_num << std::endl;
return 0;
}
对于 const 变量,如果想在多个源文件共享。声明 处加extern 是必须的,且定义处也必须加 extern,注意,是源文件间共享,也就是.cpp文件之间共享
如果是头文件里面直接定义const变量,可加extern也可不加
一般,我们会将一些供多个文件使用的变量放在一个头文件,而这些变量通常是const变量
config.h
cpp
const int aaaa = 10;
注意,在头文件中,在头文件中,在头文件中,重要的事情说三遍
非const变量只能在头文件声明,不能定义
下面的写法是错误的:
int test_int = 10;
只能声明,如这么写:extern int test_int;
对的,你还得加上extern,因为你如果只在头文件写了int test_int ;编译器还是认为你定义了一个初始值为0的int
但是,一般我们不会在头文件声明一个普通变量,只是给大家讲解一下注意事项
TestConfig1.cpp
cpp
#include <iostream>
#include "config.h"
void print_from_file1() {
std::cout << "[file1] aaaa 的值: " << aaaa << ", aaaa 的地址: " << &aaaa << std::endl;
}
TestConfig2.cpp
cpp
#include <iostream>
#include "config.h"
void print_from_file2() {
std::cout << "[file2] aaaa 的值: " << aaaa << ", aaaa 的地址: " << &aaaa << std::endl;
}
main.cpp
cpp
// 声明其他文件里的函数
void print_from_file1();
void print_from_file2();
int main() {
print_from_file1();
print_from_file2();
return 0;
}
可以看到,在cpp中,如果const变量写在头文件里面,const变量不需要写extern 就可以把这个变量给多个文件使用,只要需要使用这个变量的源文件引用头文件就可以了。
但是
看一下运行结果
cpp
[file1] aaaa 的值: 10, aaaa 的地址: 0x7ff712a49320
[file2] aaaa 的值: 10, aaaa 的地址: 0x7ff712a49350
可以看到不同文件的aaa的值的内存是不一样的,它们不是同一个变量。这也是文章开头说的,这是 C++ 为了防止头文件中定义 const 变量导致多重定义冲突而设计的。
如果需要aaa都是同一个内存以节约一点内存,可以在声明处都加上extern
config.h
cpp
// 直接在头文件中定义 const 变量
extern const int aaaa = 100;
TestConfig1.cpp
cpp
#include <iostream>
#include "config.h"
void print_from_file1() {
std::cout << "[file1] aaaa 的值: " << aaaa << ", aaaa 的地址: " << &aaaa << std::endl;
}
TestConfig2.cpp
cpp
#include <iostream>
#include "config.h"
void print_from_file2() {
std::cout << "[file2] aaaa 的值: " << aaaa << ", aaaa 的地址: " << &aaaa << std::endl;
}
main.cpp
cpp
#include "config.h"
#include <windows.h> // 必须包含这个头文件,使vscode支持中文输出
// 声明其他文件里的函数
void print_from_file1();
void print_from_file2();
//定义这里不必加extern
const int aaaa = 30;
int main() {
// 设置控制台输出编码为 UTF-8
SetConsoleOutputCP(65001);
print_from_file1();
print_from_file2();
return 0;
}
将aaaa的定义放在了main.cpp里面了
运行结果
cpp
[file1] aaaa 的值: 30, aaaa 的地址: 0x7ff748439000
[file2] aaaa 的值: 30, aaaa 的地址: 0x7ff748439000
可以看到,aaaa的地址是同一个,它们是同一个变量