所划分的内存区块有?
在执行 C++ 程序时候,所划分出的内存区块主要有四个:
- 代码区:存放着程序的二进制代码,由操作系统管理。
- 全局区:存放全局变量、静态变量,以及常量(字符常量和
const 修饰的全局变量)。
- 栈区:存放所有的局部变量,其空间分配与释放由编译器管理,当函数结束,局部变量自动被释放。
- 堆区:存放所有动态开辟的变量,其空间分配与释放由程序员管理。
在 exe 程序执行前只有代码区和全局区,执行时才具有栈区与堆区。
代码区解析
代码区里面存放的是 .exe 二进制机器指令,具有两个特性:
- 共享性:内存中只有一份程序代码,多个进程/线程可共享,节约空间。
- 只读性:不允许被修改。
全局区解析
全局区存的是全局变量、静态变量以及常量,下面通过示例演示。
1 全局变量的地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <iostream> using namespace std;
int ga = 10; int gb = 20; int gc = 20;
int main() { cout << "全局变量ga的地址是" << &ga << endl; cout << "全局变量gb的地址是" << &gb << endl; cout << "全局变量gc的地址是" << &gc << endl; return 0; }
|
运行可见全局变量的地址通常很接近(在同一内存段)。
2 静态变量的地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <iostream> using namespace std;
int ga = 10; int gb = 20; int gc = 20;
int main() { static int sa = 10; static int sb = 20; static int sc = 20;
cout << "全局变量ga的地址是" << &ga << endl; cout << "全局变量gb的地址是" << &gb << endl; cout << "全局变量gc的地址是" << &gc << endl;
cout << "静态变量sa的地址是" << &sa << endl; cout << "静态变量sb的地址是" << &sb << endl; cout << "静态变量sc的地址是" << &sc << endl; return 0; }
|
静态变量与全局变量的地址也会非常接近,因为它们都存在全局区域。
3 常量(字符常量及 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 25 26 27 28 29 30 31 32 33 34 35 36 37
| #include <iostream> using namespace std;
int ga = 10; int gb = 20; int gc = 20;
const int cga = 10; const int cgb = 10; const int cgc = 10;
int main() { static int sa = 10; static int sb = 20; static int sc = 20;
cout << "全局变量ga的地址是" << &ga << endl; cout << "全局变量gb的地址是" << &gb << endl; cout << "全局变量gc的地址是" << &gc << endl; cout << endl;
cout << "静态变量sa的地址是" << &sa << endl; cout << "静态变量sb的地址是" << &sb << endl; cout << "静态变量sc的地址是" << &sc << endl; cout << endl;
cout << "字符常量1的地址是" << &"123" << endl; cout << "字符常量2的地址是" << &"124" << endl; cout << "字符常量3的地址是" << &"125" << endl; cout << endl;
cout << "const 修饰的全局变量 cga 地址是" << &cga << endl; cout << "const 修饰的全局变量 cgb 地址是" << &cgb << endl; cout << "const 修饰的全局变量 cgc 地址是" << &cgc << endl; return 0; }
|
可以看到全局常量与全局变量通常位于同一或相近的内存区段。
栈区解析
栈区存放的是局部变量,下面通过对比展示全局与局部变量地址差异。
1 普通局部变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| #include <iostream> using namespace std;
int ga = 10; int gb = 20; int gc = 20;
int main() { int la = 10; int lb = 20; int lc = 20;
cout << "全局变量ga的地址是" << &ga << endl; cout << "全局变量gb的地址是" << &gb << endl; cout << "全局变量gc的地址是" << &gc << endl; cout << endl;
cout << "局部变量la的地址是" << &la << endl; cout << "局部变量lb的地址是" << &lb << endl; cout << "局部变量lc的地址是" << &lc << endl; return 0; }
|
局部变量地址与全局变量地址差距较大,说明全局区与栈区是不同区域。
2 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 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| #include <iostream> using namespace std;
int ga = 10; int gb = 20; int gc = 20;
const int cga = 10; const int cgb = 10; const int cgc = 10;
int main() { int a = 10; int b = 10; int c = 10; const int ca = 10; const int cb = 20; const int cc = 30;
cout << "全局变量ga的地址是" << &ga << endl; cout << "全局变量gb的地址是" << &gb << endl; cout << "全局变量gc的地址是" << &gc << endl; cout << endl;
cout << "const 修饰的全局变量 cga 地址是(全局常量)" << &cga << endl; cout << "const 修饰的全局变量 cgb 地址是(全局常量)" << &cgb << endl; cout << "const 修饰的全局变量 cgc 地址是(全局常量)" << &cgc << endl; cout << endl;
cout << "局部变量a的地址是" << &a << endl; cout << "局部变量b的地址是" << &b << endl; cout << "局部变量c的地址是" << &c << endl; cout << endl;
cout << "const 修饰的局部变量 ca 的地址是(局部常量)" << &ca << endl; cout << "const 修饰的局部变量 cb 的地址是(局部常量)" << &cb << endl; cout << "const 修饰的局部变量 cc 的地址是(局部常量)" << &cc << endl; return 0; }
|
全局 const 常量与局部 const 常量所属区域可能不同,局部 const 常量通常属于栈区。
3 栈区注意事项
栈区里的变量不可返回其地址(返回后该栈空间可能会被释放)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #include <iostream> using namespace std;
int* func() { int a = 10; return &a; }
int main() { int* p = func(); cout << "*p 的值是" << *p << endl; return 0; }
|
某些编译器(例如文中提到的 VS2019)在某些情况下可能会让第一次错误使用看似“成功”,但这是未定义行为,不可依赖。
堆区解析
堆区的开辟与释放由程序员自己执行,开辟一般用 new,释放一般用 delete。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <iostream> using namespace std;
int* func() { int* p = new int(10); return p; }
int main() { int* p = func(); cout << "func 的值是" << *p << endl; cout << "func 的值是" << *p << endl; cout << "func 的值是" << *p << endl; delete p; return 0; }
|
堆区变量在 delete 之前其值不受函数返回影响。
new 的用法
1 开辟单个堆区元素
语法
- 开辟:
type* name = new type(content); 其中 type 是元素类型,content 是元素内容,name 是变量名。
- 释放:
delete name。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| #include <iostream> using namespace std;
int main() { int* p1 = new int(10); float* p2 = new float(20.12); char* p3 = new char('w'); cout << "整型元素的值是" << *p1 << endl; cout << "浮点元素的值是" << *p2 << endl; cout << "字符元素的值是" << *p3 << endl; delete p1; delete p2; delete p3; return 0; }
|
2 开辟数组
语法
- 开辟:
type* name = new type[size];
- 释放:
delete [] name (必须带方括号)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <iostream> using namespace std;
int main() { int* p1 = new int[10]; char* p2 = new char[10]; for (int i = 0; i < 10; i++) p1[i] = i + 10; for (int i = 0; i < 10; i++) p2[i] = i + 65;
for (int i = 0; i < 10; i++) cout << p1[i] << ' '; cout << endl; for (int i = 0; i < 10; i++) cout << p2[i] << ' ';
delete [] p1; delete [] p2; return 0; }
|
参考文献