在 C++ 语言中,程序运行时的内存通常被划分为以下几个区域:
- 代码区(Text Segment)
- 常量区(Constant Segment)
- 全局/静态区(Data Segment,包含静态数据段和 BSS 段)
- 堆区(Heap)
- 栈区(Stack)
分布图例如下:
1. 代码区(Text Segment)
- 该区域存放的是程序的机器指令,即代码本身。
- 代码区通常是只读的(read-only),防止程序在运行过程中修改自身代码,提高安全性。
- 在程序启动时由操作系统加载到内存,并且通常所有线程共享这部分内存。
2. 常量区(Constant Segment)
- 该区域用于存储只读的常量,例如字符串字面量和
const
关键字修饰的全局常量。 - 常量区通常和代码区一样是只读的,防止意外修改。
- 例如:
const int x = 10; // 存储在常量区
const char* str = "Hello"; // "Hello" 字符串存储在常量区
- 但要注意,
const
变量并不一定存储在常量区,例如:
void func() {
const int local_const = 5; // 该常量存储在栈区
}
这里 local_const
是局部 const
变量,它仍然存储在栈区,而不是常量区。
3. 全局/静态区(Data Segment)
- 该区域存储全局变量、静态变量(
static
修饰的变量)。 - 进一步细分为:
-
- 已初始化数据段(Data Segment):存放已初始化的全局变量和静态变量。
- 未初始化数据段(BSS Segment):存放未初始化的全局变量和静态变量,程序运行时会自动初始化为 0。
示例:
int global_var = 42; // 存在已初始化数据段
static int static_var = 10; // 存在已初始化数据段
int uninitialized_global; // 存在 BSS 段,默认值为 0
static int uninitialized_static; // 存在 BSS 段,默认值为 0
区别:
- 全局/静态变量的生命周期贯穿整个程序运行时间,直到程序退出才被释放。
- 这些变量存储在可读写的内存区域,不同于代码区和常量区的只读特性。
4. 堆区(Heap)
- 堆区用于动态分配的内存,
new
或malloc
分配的对象存储在这里。 - 由程序员手动管理,如果
new
了对象,必须delete
,否则会造成内存泄漏。 - 例如:
int* p = new int(5); // 在堆区分配一个 int
delete p; // 释放堆内存
- 注意:
-
- 堆的管理通常由 C++ 运行时库(Runtime Library)和操作系统负责。
- 堆区不像栈区那样自动释放,程序员需要自己管理内存的分配和释放。
5. 栈区(Stack)
- 栈区存储函数调用时的局部变量、函数参数、返回地址等数据。
- 由系统自动管理,函数调用时分配,函数返回时自动释放。
- 栈的分配和回收速度比堆快,因此局部变量的存取效率更高。
- 例如:
void func() {
int a = 10; // 局部变量 a 存在栈区
}
- 注意:
-
- 栈的空间有限,过度使用递归或分配过大的局部数组可能导致栈溢出(Stack Overflow)。
常量区 vs. 全局/静态区
两者的主要区别如下:
类别 | 存储内容 | 是否可修改 | 存储位置 | 生命周期 |
常量区 | 字符串字面量、全局 变量 | 只读 | 代码段的一部分 | 程序运行期间 |
全局/静态区 | 全局变量、静态变量 | 可读写 | Data Segment(已初始化/未初始化数据段) | 程序运行期间 |
关键点:
- 常量区是只读的,而全局/静态区是可修改的。
- 字符串字面量存储在常量区,而普通全局/静态变量存储在全局/静态区。
- 局部
const
变量存储在栈区,而不是常量区。
全局变量和静态变量的区别
在 C++ 中,全局变量和静态变量的存储位置都属于**“全局/静态存储区”**(Data Segment),但它们之间还是有一些区别的。我们通常把这部分内存区域分成两个概念:
- 全局区(Global Segment)——存储全局变量
- 静态区(Static Segment)——存储静态变量
不过在实际实现上,这两个变量通常都位于全局/静态存储区(Data Segment),只是它们的作用域和访问方式不同。
1. 全局变量(Global Variables)
- 存储位置:存放在全局区(Data Segment 的一部分)。
- 作用域:在整个程序范围内可访问,即在定义它的文件及其他文件中(如果使用
extern
声明)。 - 生命周期:从程序启动到程序终止,一直存在,不会被销毁。
- 初始化:
-
- 显式初始化:按照程序员指定的值初始化。
- 默认初始化:如果未初始化,全局变量会被自动初始化为 0(整数)、
nullptr
(指针)、0.0
(浮点数)。
- 示例:
#include <iostream>
int globalVar = 10; // 全局变量,存储在全局区
void func() {
std::cout << globalVar << std::endl; // 在任何地方都可以访问
}
int main() {
func(); // 输出 10
return 0;
}
- 可被
extern
关键字在其他文件中引用:
// file1.cpp
int globalVar = 42;
// file2.cpp
extern int globalVar; // 在其他文件中声明
2. 静态变量(Static Variables)
静态变量可以分为静态局部变量和静态全局变量:
(1) 静态全局变量(Static Global Variables)
- 存储位置:存放在静态区(Data Segment 的一部分)。
- 作用域:仅限于定义它的文件内部(文件作用域),不能被其他文件
extern
访问。 - 生命周期:从程序启动到程序终止,始终存在。
- 初始化:
-
- 显式初始化:按照程序员指定的值初始化。
- 默认初始化:如果未初始化,静态全局变量会被自动初始化为 0、
nullptr
或0.0
。
- 示例:
#include <iostream>
static int staticGlobalVar = 20; // 静态全局变量
void func() {
std::cout << staticGlobalVar << std::endl; // 可以访问
}
int main() {
func(); // 输出 20
return 0;
}
- 不能被
extern
关键字访问:
// file1.cpp
static int staticVar = 42; // 只能在 file1.cpp 内部访问
// file2.cpp
extern int staticVar; // ❌ 错误,无法访问
(2) 静态局部变量(Static Local Variables)
- 存储位置:存放在静态区(Data Segment 的一部分),不会存放在栈上。
- 作用域:仅限于函数内部,但不会在函数调用结束后销毁,下一次调用仍然能访问原来的值。
- 生命周期:在程序运行期间一直存在,直到程序结束。
- 初始化:
-
- 只在函数首次调用时初始化一次,后续调用不会重新初始化。
- 示例:
#include <iostream>
void func() {
static int counter = 0; // 静态局部变量,初始化仅执行一次
counter++;
std::cout << "Counter: " << counter << std::endl;
}
int main() {
func(); // 输出 Counter: 1
func(); // 输出 Counter: 2
func(); // 输出 Counter: 3
return 0;
}
-
counter
变量即使func()
结束了,也不会被销毁。- 普通局部变量(非 static) 每次调用都会重新初始化,而静态局部变量的值会被保留。
3. 静态区和全局区的区别
(1)存储位置
- 全局变量 存储在全局区(Data Segment 的一部分)。
- 静态变量 存储在静态区(Data Segment 的一部分)。
实际上,全局变量和静态变量都在数据段(Data Segment),但它们的作用域不同,因此有时被称为“全局区”和“静态区”。
(2)作用域
变量类型 | 作用域(访问范围) | 可否用 extern 访问 |
全局变量 | 整个程序都可以访问 | ✅ 可以使用 |
静态全局变量 | 仅限于当前文件(文件作用域) | ❌ 不能跨文件访问 |
静态局部变量 | 仅限于当前函数(局部作用域) | ❌ 不能跨函数访问 |
(3)生命周期
变量类型 | 生命周期 |
全局变量 | 程序运行期间一直存在 |
静态全局变量 | 程序运行期间一直存在 |
静态局部变量 | 程序运行期间一直存在(不会随着函数结束而销毁) |
普通局部变量 | 函数调用时创建,调用结束后销毁 |
(4)初始化
- 全局变量 和 静态变量(全局或局部)如果未手动初始化,会自动初始化为 0 或
nullptr
。 - 普通局部变量(非 static) 未初始化时,值是未定义的(随机值)。
4. 总结
- 全局变量存放在全局区(Data Segment),静态变量存放在静态区(Data Segment),但它们都属于“全局/静态存储区”。
- 静态变量包括静态全局变量和静态局部变量:
-
- 静态全局变量 作用域仅限当前文件(不能用
extern
)。 - 静态局部变量 作用域仅限当前函数,但生命周期贯穿整个程序运行。
- 静态全局变量 作用域仅限当前文件(不能用
- 静态变量的生命周期比普通局部变量长,即使函数调用结束,静态局部变量的值也不会被销毁,而普通局部变量会被销毁。
- 全局变量可以被
extern
访问,静态全局变量不行。 - 静态变量初始化只执行一次,而普通局部变量每次函数调用都会重新初始化。
总结
- C++ 的内存布局分为 代码区、常量区、全局/静态区、堆区和栈区,各有不同的作用和生命周期。
- 常量区与全局/静态区不同,常量区通常是只读的,而全局/静态区可以修改。
- 全局变量和静态变量的生命周期与程序一致,而局部变量在栈上,函数返回时就会被销毁。
- 堆区用于动态分配的内存,需要手动释放,否则可能会发生内存泄漏。
- 栈区用于局部变量和函数调用数据,系统自动分配和回收,但栈的空间有限,可能会发生栈溢出。