所划分的内存区块有?

在执行 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; // g 是 global 的意思
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; // c 表示 const
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; // l 是 local 的意思
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;
}

参考文献