【堆内存&栈内存】【stack和heap的区别】 【C++对内存区的五种划分】【 可以开辟数组的大小】

0 篇文章 0 订阅

1. stack和heap的区别

1 heap和stack存储在哪里?

RAM(Radom Access Memory)

2 线程和heap stack之间的关系

在多线程的应用中,每一个线程都拥有一个属于自己的stack。但是所有这些线程都共享一个heap。因此,必须存在一些协调机制使得这些线程不会同时访问或操作heap。

3 一个对象object可以存在stack而不是heap上吗?

可以,如果你在一个函数内create一个object而没有用new,那么这个object就存在stack上。如果这个函数运行完,那么stack上的object对应的内存也会被remove掉。
如果希望在函数中create一个存在heap上的object,那么就要用new来实现。注意,如果用new开辟了一片空间,那么也必须要认为的delete掉这篇空间,否则将导致memory leak-占据已经无用的空间。

4 stack和heap上的memory生存时间。

stakc上的空间当函数运行完自动释放,而heap上的空间只能由程序员delete才释放。

5 stack和heap可以grow in size吗?

stack的大小是固定的,所以如果分配给stack的空间过大,就会stack overflow,一般出现在无限递归的情况下。
而heap的当前空间不够用时,os就会再分配给heap一些空间。这一点是heap和stack的显著区别之一。

6 stack和heap是如何实现的?

depends on语言,编译器,以及runtime。具体细节千差万别,但是总体上都是完成了相同的功能。

7 那个更快?为什么? 分配效率?

stack更快,因为stack的空间分配只需要移动指针。

7.1 另外详细参考:点我

栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的 效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系 统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就 有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。
从这里我们可以看到,堆和栈相比,由于大量 new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发 用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地 址,EBP和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。
虽然栈有如此众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。

7.2 static用来控制变量的存储方式和可见性

函数内部定义的变量,在程序执行到它的定义处时,编译器为它在栈上分配空间,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问 题: 如果想将函数中此变量的值保存至下一次调用时,如何实现? 最容易想到的方法是定义一个全局的变量,但定义为一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅受此 函数控制)。

需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见。

静态数据成员要在程序一开始运行时就必须存在。因为函数在程序运行中被调用,所以静态数据成员不能在任何函数内分配空间和初始化。

这样,它的空间分配有三个可能的地方,一是作为类的外部接口的头文件,那里有类声明;二是类定义的内部实现,那里有类的成员函数定义;三是应用程序的main()函数前的全局数据声明和定义处。

静态数据成员要实际地分配空间,故不能在类的声明中定义(只能声明数据成员)。类声明只声明一个类的“尺寸和规格”,并不进行实际的内存分配,所以在类声明中写成定义是错误的。它也不能在头文件中类声明的外部定义,因为那会造成在多个使用该类的源文件中,对其重复定义。

static被引入以告知编译器,将变量存储在程序的静态存储区而非栈上空间,静态数据成员按定义出现的先后顺序依次初始化,注意静态成员嵌套时,要保证所嵌套的成员已经初始化了。消除时的顺序是初始化的反顺序。

static的优势
可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,这样可以提高时间效率。

7.3 static注意事项

(1) 类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致了它仅能访问类的静态数据和静态成员函数。

(2) 不能将静态成员函数定义为虚函数。

(3) 由于静态成员声明于类中,操作于其外,所以对其取地址操作,就多少有些特殊,变量地址是指向其数据类型的指针,函数地址类型是一个“nonmember函数指针”。

(4) 由于静态成员函数没有this指针,所以就差不多等同于nonmember函数,结果就产生了一个意想不到的好处:成为一个callback函数,使得我们得以将C++和C-based X Window系统结合,同时也成功的应用于线程函数身上。

(5) static并没有增加程序的时空开销,相反她还缩短了子类对父类静态成员的访问时间,节省了子类的内存空间。

(6) 静态数据成员在<定义或说明>时前面加关键字static。

(7) 静态数据成员是静态存储的,所以必须对它进行初始化。

(8) 静态成员初始化与一般数据成员初始化不同:
  
   初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆;

初始化时不加该成员的访问权限控制符private,public等;

初始化时使用作用域运算符来标明它所属类;

所以我们得出静态数据成员初始化的格式:

<数据类型><类名>::<静态数据成员名>=<值>

(9) 为了防止父类的影响,可以在子类定义一个与父类相同的静态变量,以屏蔽父类的影响。这里有一点需要注意:我们说静态成员为父类和子类共享,但 我们有重复定义了静态成员,这会不会引起错误呢?不会,我们的编译器采用了一种绝妙的手法:name-mangling 用以生成唯一的标志。

8 stack和heap可能会出现什么问题?

sack空间不够用会stackoverflow,导致程序崩溃。而heap可能会出现fragamentation(碎片化),空间利用效率低下。

9 我应该用哪个?

当明确知道你要使用的空间时,如果不大的话就用stack,如果较大或者无法暂时确定,就用heap。

2. C++对内存区的五种划分

内存分配方式

  • C/C++主要有以下五种内存存储区:

    全局/静态存储区域:用来存储全局变量,静态变量。程序编译时内存已分配好,并存在于程序整个运行期间,程序结束后由系统统一释放 全局变量和静态变量被分配到同一块内存中。

    • C 语言中,全局变量又分为初始化的和未初始化的。初始化的全局变量和静态变量在一块区域,未初始化的全局变量与静态变量在相邻的另一块区域。同时未被初始化的对象存储区可以通过 void* 来访问和操纵,程序结束后由系统自行释放。在 C++ 里面没有区分,他们共同占用同一块内存区。

    栈:存放函数的参数值,局部变量,函数执行结束时会被自动释放。栈内存分配运算内置于处理器的指令集中,效率高,但是容量有限。

    堆(动态内存分配):通过new和malloc由低到高分配,由delete或free手动释放或者程序结束自动释放。动态内存的生存期人为决定,使用灵活。缺点是容易分配/释放不当容易造成内存泄漏,频繁分配/释放会产生大量内存碎片。 若程序员不释放,程序结束时可能由OS(操作系统)回收。

    字符/文字常量区: 存放常量字符串,程序结束时由系统释放程序代码区: 存放函数体的二进制代码。

    程序代码区: 存放函数体的二进制代码。

例子

一个由C/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其
操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回
收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的
全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另
一块区域。 - 程序结束后由系统释放。
4、文字常量区 —常量字符串就 是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。

这是一个前辈写的,非常详细

 //main.cpp    
  int   a   =   0;   全局初始化区    
  char   *p1;   全局未初始化区    
  main()    
  {    
  int   b;   栈    
  char   s[]   =   "abc";   栈    
  char   *p2;   栈    
  char   *p3   =   "123456";   123456/0在常量区,p3在栈上。    
  static   int   c   =0;   全局(静态)初始化区    
  p1   =   (char   *)malloc(10);    
  p2   =   (char   *)malloc(20);    

分配得来得10和20字节的区域就在堆区。
strcpy(p1, “123456”); 123456/0放在常量区,编译器可能会将它与p3所指向的"123456"
优化成一个地方。

函数内申请的变量,数组,是在栈(stack)中申请的一段连续的空间。栈的默认大小为2M或1M,开的比较小。

全局变量,全局数组,静态数组(static)则是开在全局区(静态区)(static)。大小为2G,所以能够开的很大。

而malloc、new出的空间,则是开在堆(heap)的一段不连续的空间。理论上则是硬盘大小。


#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
				//在本人环境中
int c[20000][20000]; //全局数组能开到20000*20000
int main()
{
	int b[1024*505];
	int b2[700*700];
	char a[4*518028];
	int b1[500000]; //5*10^5  函数中的char数组最大能开4*518028,int最大能开到518028。
	static int c[20000][20000]; //static能开到10^7*10^7。       注意 static和 全局开的是同一块空间
	printf("1");

参考

https://blog.csdn.net/a479778594/article/details/70157121

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值