服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C++内存四区之代码区、全局区、栈区和堆区

C++内存四区之代码区、全局区、栈区和堆区

2021-11-21 15:58ZhiboZhao C/C++

C++编译器会把代码直接分为四个小区,弄懂这四小区对我们理解内存有所帮助,所以下面这篇文章主要给大家介绍了关于C++内存四区之代码区、全局区、栈区和堆区的相关资料,需要的朋友可以参考下

C++内存四区

C++ 在程序执行时,将内存大致分为代码区,全局区,栈区和堆区四个区域。不同的区域存储不同的数据,赋予不同的生命周期,能够更灵活地进行编程。

  1. 代码区:存放函数体的二进制代码,由操作系统管理创建,代码区时共享的,对于频繁被执行的程序,只需要存有一份代码即可;
  2. 全局区:存放全局变量和静态变量以及常量,在程序结束后由操作系统释放;
  3. 栈区:由编译其自动分配释放,存放函数的参数值以及局部变量等;
  4. 堆区:一般由程序员通过 new 开辟空间,进行分配和释放,若程序员不释放,则程序结束时由操作系统回收

下面通过一个例子对全局区,栈区,堆区的数据声明周期进行说明:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 全局变量属于全局区,由操作系统管理释放
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 都属于堆区。由于栈区的数据在程序运行结束后会被编译器自动销毁,因此不要返回局部变量的地址,举例如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
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 中的数据。

相反,堆区数据由程序员自己进行管理,在程序执行完之后并不会自动释放。当整个程序执行完毕之后会由操作系统释放。

?
1
2
3
4
5
6
7
8
9
10
11
12
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

附:内存四区的小结

1、栈区(stack):就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是函数的返回地址、参数、局部变量、返回值等,从高地址向低地址增长。在一个进程中,位于用户虚拟地址空间顶部的是用户栈,编译器用它来实现函数的调用。其操作方式类似于数据结构中的栈。

2、堆区(heap): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,在程序运行过程中可以动态增加堆大小(移动break指针),从低地址向高地址增长。

        堆:是操作系统所维护的一块特殊内存,它提供了动态分配的功能,当运行程序调用malloc()时就会从中分配,稍后调用free()可把内存交还。

        自由存储区:自由存储是C++中通过new和delete动态分配和释放对象的抽象概念,通过new来申请的内存区域可称为自由存储区。基本上,所有的C++编译器默认使用堆来实现自由存储,也即是缺省的全局运算符new和delete也许会按照malloc和free的方式来被实现,这时藉由new运算符分配的对象,说它在堆上也对,说它在自由存储区上也正确。但程序员也可以通过重载操作符,改用其他内存来实现自由存储,例如全局变量做的对象池,这时自由存储区和堆区就有区别了。

3、数据区:主要包括静态全局区和静态区,如果要站在汇编角度细分的话还可以分为很多小的区。

        全局区&静态区:全局变量和静态变量被分配到同一块内存中,在以前的 C 语言中,全局变量和静态变量又分为

    全局初始化区(DATA段) :存储程序中已初始化的全局变量和静态变量

    未初始化段(BSS段) :存储未初始化的全局变量和静态变量(局部+全局)。BSS段在DATA段的相邻的另一块区域。

              BBS段特点:在程序执行前BBS段自动清零,所以未初始化的全局变量和静态变量在程序执行前已经成为0。

  在 C++ 里面没有这个区分了,他们共同占用同一块内存区。

4、代码区:包括只读存储区和文本区,其中只读存储区存储字符串常量,就是常量区,文本区存储程序的机器代码。

那“内存四区”和“内存五区”有什么区别吗?

其实“内存四区”和“内存五区”指的东西都是完全一样的

内存五区为:栈区、堆区、全局区(静态区)、常亮区、代码区

内存四区为:栈区、堆区、数据区(全局区(静态区)、常亮区)、代码区

因此从上面可以看出,对于内存四区而言,其只是把全局区(静态区)和常亮区合并为一个数据区而已,其实内容都是完全一样的

总结

到此这篇关于C++内存四区之代码区、全局区、栈区和堆区的文章就介绍到这了,更多相关C++内存四区内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://www.cnblogs.com/zhaozhibo/p/14941915.html

延伸 · 阅读

精彩推荐