共享变量一 关于extern与const

在 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的地址是同一个,它们是同一个变量

相关推荐
FRing034 小时前
JavaScript入门到精通三十二(深拷贝、浅拷贝、递归函数)
开发语言·前端·javascript
小嘉丶学长5 小时前
Day14 · 字典与集合
java·开发语言·网络·python·学习·1024程序员节
迪菲赫尔曼5 小时前
【基础篇 11】把官方源码放到服务器:版本、解压层级和安装位置,一次弄清楚
运维·服务器·开发语言·人工智能·python·yolo·目标检测
chari克里5 小时前
QT5.15.2 + msvc2022部署全流程说明(windows可联网环境)
开发语言·c++·qt
Hello.Reader6 小时前
tsrs 技术解析用 Rust 移植 TypeScript 7,类型检查还能怎样提速?
开发语言·rust·typescript
透明的玻璃杯6 小时前
VS2015+QT 多线程
开发语言·qt
码匠许师傅6 小时前
【C++三方组件】mbedTLS:面向嵌入式的轻量 TLS
开发语言·c++
迪菲赫尔曼7 小时前
【基础篇 12】把环境与源码接起来:检查 GPU,再安装服务器上的项目
运维·服务器·开发语言·人工智能·python·yolo·目标检测
?re?ta?rd?ed?7 小时前
C++11新特性,{}与左值
c++·windows