内存四区之代码区,全局区,栈区和堆区
C++ 在程序执行时,将内存大致分为代码区,全局区,栈区和堆区四个区域。不同的区域存储不同的数据,赋予不同的生命周期,能够更灵活地进行编程。
- 代码区:存放函数体的二进制代码,由操作系统管理创建,代码区时共享的,对于频繁被执行的程序,只需要存有一份代码即可;
- 全局区:存放全局变量和静态变量以及常量,在程序结束后由操作系统释放;
- 栈区:由编译其自动分配释放,存放函数的参数值以及局部变量等;
- 堆区:一般由程序员通过
new
开辟空间,进行分配和释放,若程序员不释放,则程序结束时由操作系统回收
下面通过一个例子对全局区,栈区,堆区的数据声明周期进行说明:
// 全局变量属于全局区,由操作系统管理释放
int g_a = 1;
int g_b = 2;
int main(void)
{
cout << "g_a 的地址为:\t"<< int(&g_a) << endl;
cout << "g_b 的地址为:\t" << int(&g_b) << endl;
// 创建普通的局部变量,属于栈区
int a = 10;
int b = 20;
cout << "a 的地址为:\t" << int(&a) << endl;
cout << "b 的地址为:\t" << int(&b) << endl;
// 创建静态变量,属于全局区
static int s_a = 40;
static int s_b = 50;
cout << "s_a 的地址为:\t" << int(&s_a) << endl;
cout << "s_b 的地址为:\t" << int(&s_b) << endl;
// 程序员自己创建变量,属于堆区
int* d_a = new int(10);
int* d_b = new int(20);
cout << "d_a 的地址为:\t" << int(d_a) << endl;
cout << "d_b 的地址为:\t" << int(d_b) << endl;
}
输出结果为:
g_a 的地址为: 5300224 g_b 的地址为: 5300228
a 的地址为: 6421316 b 的地址为: 6421304
s_a 的地址为: 5300232 s_b 的地址为: 5300236
d_a 的地址为: 9547944 d_b 的地址为: 9547992
我们从中可以看到,g_a
,g_b
,s_a
,s_b
都属于全局区,同理,a
,b
都属于栈区,d_a
,d_b
都属于堆区。由于栈区的数据在程序运行结束后会被编译器自动销毁,因此不要返回局部变量的地址,举例如下:
int* func()
{
int a = 10; // 栈区数据,在程序执行完之后自动释放
return &a; //虽然返回了a的地址,然而数据在func结束时已经被销毁
}
int main(void)
{
int* a = func(); // 此时a表示在函数func在栈区开辟的地址,但是其中的数据已被销毁
cout << "a 的地址为:\t" << int(a) << "a 存放的数据为:\t" << *a << endl;
cout << "a 的地址为:\t" << int(a) << "a 存放的数据为:\t" << *a << endl;
}
输出结果为:
a 的地址为: 7601480a 存放的数据为: 10
a 的地址为: 7601480a 存放的数据为: 2084553696
由于编译器会对栈区的数据做一次保留,因此第一条的 cout
语句能够正常输出,然而第二次的输出才是内存地址 a
中的数据。
相反,堆区数据由程序员自己进行管理,在程序执行完之后并不会自动释放。当整个程序执行完毕之后会由操作系统释放。
int* func()
{
int * a = new int(10); // 程序员使用new在堆区开辟空间,在程序执行完之后自动释放
return a; //同样返回了a的地址,然而只要程序没有运行结束,除非程序员释放,否则会一直保留
}
int main(void)
{
int* a = func(); // 此时a表示在函数func在堆区开辟的地址,编译器无法自动销毁
cout << "a 的地址为:\t" << int(a) << "a 存放的数据为:\t" << *a << endl;
cout << "a 的地址为:\t" << int(a) << "a 存放的数据为:\t" << *a << endl;
}
输出结果为:
a 的地址为: 23507016a 存放的数据为: 10
a 的地址为: 23507016a 存放的数据为: 10