C++变量内存存储
C++作为底层的高效语言,最大的特色就是深入字节级的内存操作,通过指针的运行,把控计算机的内存使用率,提高程序的运行速率和节省空间使用率。
而为了达成这样的效果,C++并不是自动回收内存的,而是开发者自己回收内存的,所以经常发生stackoverflow的错误,为了避免这种错误,我们首先要明白C++的变量在内存中是如何存储的。
C++的两个阶段
C++分为两个阶段编译阶段、运行阶段,首先是编译阶段,将C++代码编译成机器码,而后是运行阶段在内存上运行机器码。
而变量内存的分配存储也是可以在这两个阶段进行的。在编译阶段进行内存分配,变量会提前分配好内存,这种分配一般都是固定大小,后续不能更改,在编译的适合就已经申请了内存位置。另外一种是运行阶段进行内存分配,这种分配是动态的,编译的时候不会申请内存,等机器码运行的时候,再确定变量的内存大小,这两种方法各有利弊。在C++的OOP编程思维里面,推荐使用的还是后者。
变量存储方法
自动存储
int a = 1;
float b = 0.5;
char f;
f = 'w';
typename name;
自动存储是最常见的变量存储方法,它的优点是不需要我们去担心内存回收的问题。因为自动存储的变量生命周期只在当前的代码块,也就是当前的花括号里面。一旦这部分代码块运行结束,该花括号里面声明的变量都会自动回收。
这种变量存储声明的方法就是最常见的方法,直接声明定义。自动存储的变量内存都会分配到栈上,分配阶段就是编译阶段的时候就已经分配好了,到执行到该代码块的时候,变量会依次依次进入栈内存,结束是再依次释放,遵守先进后出
原则。所以在实际程序运行的时候,栈会不断增大和缩小。
静态存储
静态存储本质上和自动存储是一样的,是存储在数据段上的变量,唯一的区别就是静态存储有着更长的生命周期,一般存在于整个程序运行过程中,所以静态存储一般都是全局变量,或者是类似于static int a = 5;
,用特定的关键词声明的变量。
动态存储
这种存储是C++中最难也是最重要的存储方式,动态存储的变量存储在堆上,而不是栈上,堆和栈的含义我们后续再讨论。动态存储的样例如下:
int* p = new int; # 定义一个整型指针
char* string = new char[]; # 定义一个字符数组指针
float a = 3.14;
float *b = &a;
这里我们知道其实动态存储就是指针变量,C++中的指针变量用法上和C语言保持一致。指针获取的是变量的地址,而不是变量本身,所以指针的内存申请是在运行阶段发生的,C++提供的new
关键词允许我们获取一个内存地址,对于数组变量而言,我们获取的是第一个元素的内存地址,我们在声明的时候不指定大小(当然也可以指定大小),我们在程序运行的时候只需要动态的在后面继续申请内存就好了,这样实现了动态数组的功能。
如下,我们有一百个商品,商品中名字最长的是50个字符,如果是自动存储的话,我们最少需要申请50x100个字符内存,但是商品中大部分的名称不超过10,那么我们就浪费了大量的内存。而动态存储很好解决了问题,我们不用申请固定大小的内存,而是根据数组的大小动态申请内存。如下:
char* b; # 动态存储变量,未申请大小
char[5] a = "bear"; # 需要存储的字符数组
b = new char[strlen(a) + 1]; # 动态申请到a大小的内存
strcpy(b, a); # 将a的值赋值给b
delete [] b; # 释放b的内存
char[20] c = "It is a good day!"; # 需要存储的字符数组
b = new char[strlen(c) + 1]; # 动态再次申请到c大小的内存
strcpy(b, c); # 将c的值赋值给b
delete [] b; # 释放b的内存
上述案例展示了动态存储变量对内存申请的灵活与自由,但是因为动态变量让我们手动自由地申请内存,我们也需要手动地释放内存。我们发现每一个new
用来申请内存,每一个delete
用来释放内存。new
和delete
是成对出现的,一定要记住释放内存,不然C++不会自动释放堆上的内存,而且因为动态内存的开放特性,使用时一定注意不要内存溢出或者泄露,这个我们下一讲再说。这里强调,动态存储变量的生命周期一定是通过new
生成,delete
结束,甚至可以在一个函数生成它,另一个函数杀死他,所以我们一定要谨慎把控它。
最后借鉴网络上的图片,看一看C++对于内存使用的情况。