C/C++ 内存四区总结
0.简单概述
C/C++程序在执行时,将内存大方向划分为4个区域(内存四区)来存放所有数据。
程序运行前产生
程序运行后产生
栈区:由编译器自动分配释放, 存放函数的参数值、局部变量等
堆区:由程序员分配和释放, 若程序员不释放,程序结束时由操作系统回收
内存四区意义:不同区域存放的数据,赋予不同的生命周期, 给我们更大的灵活编程。
按是否在全局区来划分变量和常量:
虚拟地址空间(操作系统的视角)
虚拟地址空间被分配给进程,用于存储程序代码、数据和堆栈等。
小总结:
(1).全局区存放哪些数据?
全局变量、静态变量、常量(字符串常量,const修饰的全局常量或变量)
(2).不在全局区中的有哪些?
局部变量(栈区),const修饰的局部常量或变量
(3).区分const修饰的局部常量或变量:
const
修饰的局部常量或变量在声明时必须初始化,并且一旦初始化后,就不能再改变它们的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| 修饰常量: const int MAX_VALUE = 100;
修饰指针变量: const int* ptr = &x; int* const ptr2 = &y; const int* const ptr3 = &z; int main() { int x = 5; const int* ptr = &x; int y = 15; int* const ptr2 = &y; *ptr2 = 20;
const int z = 25; const int* const ptr3 = &z; }
|
1.程序运行前
在C++程序编译后,Windows环境下生成了.exe
可执行程序,未执行该程序前分为两个区域:代码区和全局区。
程序运行前总结:
- C++中在程序运行前分为全局区和代码区
- 代码区特点是共享和只读
- 全局区中存放全局变量、静态变量、常量
- 常量区中存放 const修饰的全局常量 和 字符串常量
01.代码区
特点:只读、共享
存放 CPU 执行的机器指令(即二进制0101)
代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令;
代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码可。
02.全局区
全局变量和静态变量,还包含常量区, 字符串常量和其他常量也存放在此。
该区域的数据在程序结束后由操作系统释放。
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #include<iostream> using namespace std;
int g_a = 10; int g_b = 20;
const int c_g_a = 10; const int c_g_b = 20;
int main() { cout << "在全局区中" << endl; cout << "全局变量g_a的地址为:" << (int) &g_a << endl; cout << "全局变量g_b的地址为:" << (int) &g_b << endl;
static int s_a = 10; static int s_b = 10; cout << "静态变量s_a的地址:" << (int) &s_a << endl; cout << "静态变量s_b的地址:" << (int) &s_b << endl;
cout << "字符串常量的地址:" << (int)&"hhhhh" << endl;
cout << "const修饰的全局常量c_g_a的地址为:" << (int) &c_g_a << endl; cout << "const修饰的全局常量c_g_b的地址为:" << (int) &c_g_b << endl;
cout << "------------------------------------------------" << endl;
cout << "不在全局区中" << endl;
int a = 10; int b = 20; cout << "局部变量a的地址为:" << (int)&a << endl; cout << "局部变量b的地址为:" << (int)&b << endl;
const int c_l_a = 10; const int c_l_b = 110; cout << "const修饰的局部常量c_l_a的地址为:" << (int)&c_l_a << endl; cout << "const修饰的局部常量c_l_b的地址为:" << (int)&c_l_b << endl;
return 0; }
|
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 在全局区中 全局变量g_a的地址为:653516800 全局变量g_b的地址为:653516804 静态变量s_a的地址:653516808 静态变量s_b的地址:653516812 字符串常量的地址:653504136 const修饰的全局常量c_g_a的地址为:653503408 const修饰的全局常量c_g_b的地址为:653503412 ------------------------------------------------ 不在全局区中 局部变量a的地址为:1060109108 局部变量b的地址为:1060109140 const修饰的局部常量c_l_a的地址为:1060109172 const修饰的局部常量c_l_b的地址为:1060109204
|
2.程序运行后
程序运行后分为栈区和堆区。
01.栈区
由编译器管理开辟和释放, 存放局部变量、函数的形参等。
注意事项:不要返回局部变量的地址或引用
(1).返回局部变量的地址,warning: address of local variable ‘a’ returned
当一个函数返回指向栈内存的地址时,栈上的局部变量会被释放,这意味着返回的地址将指向一个已经不再有效的内存区域。
在后续使用该地址时,相当于访问已经释放的内存,可能会导致程序崩溃或产生不可预测的行为。
(2).返回局部变量的引用,warning: reference to local variable ‘a’ returned
当函数执行完毕后,局部变量 a
将被销毁,这意味着返回的引用将成为悬挂引用 (dangling reference)。这个操作比较危险,会导致数据不可控。
悬挂引用是指指向已经被销毁的内存的引用。
当调用者试图使用这个引用时,会访问一个无效的内存位置,导致未定义的行为,很可能会导致程序崩溃或产生不可预测的结果。
(3).解决方案:
如果函数需要返回一个局部变量的值,可以通过值传递的方式返回,
或者返回一个动态分配的内存对象,可以使用智能指针(如 std::unique_ptr
或 std::shared_ptr
)来管理动态内存的释放。
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <iostream> using namespace std;
int* func(int b) { b = 20; int a = 10; return &a; }
int& func1() { int a = 10; return a; }
int main() { int* p = func(1); cout << *p << endl; cout << *p << endl; cout << "-------------" << endl;
int& q = func1(); cout << q << endl;
return 0; }
|
修改后的函数如下,
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| int func(int b) { b = 20; int a = 10; return a; }
#include <memory> std::unique_ptr<int> func(int b) { b = 20; std::unique_ptr<int> ptr = std::make_unique<int>(10); return ptr; }
|
02.堆区
在C++中主要利用new
关键字,在堆区开辟内存;
堆区开辟的数据,由程序员手动开辟,手动释放,**释放利用操作符delete
**;若程序员不释放, 程序结束时则由操作系统回收。
语法: new 数据类型
利用new创建的数据,会返回该数据对应的类型的指针。
小总结:
堆区数据由程序员管理开辟和释放
堆区数据利用new关键字
进行开辟内存
指针是放在栈区(局部变量),指针存放的数据在堆区。
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| #include <iostream> using namespace std;
int* func() { int* p = new int(100); return p; }
void test1() { int* x = func(); cout << *x << endl; cout << *x << endl; delete x; }
void test2() { int* arr = new int[10]; for (int i = 0; i < 10; i++) { arr[i] = i + 100; cout << arr[i] << endl; } delete[] arr; }
int main() { test2(); return 0; }
|
参考资料
【视频 黑马程序员 C++教程 p84开始】