在前面的文章中,通常会涉及到几个名词,例如:栈、堆。这两个词所代表的便是计算机内存的一部分 。在计算机中,对系统的内存按照不同的使用需求进行了区分,大致可以分为:栈 、堆、数据段、代码段。其各个具体解释如下:
1. 栈又叫堆栈--非静态局部变量/函数参数/返回值等等,栈是向下增长的。
2. 堆用于程序运行时动态内存分配,堆是可以上增长的。
3. 数据段(静态区)--存储全局数据和静态数据。
4. 代码段(常量区)--可执行的代码/只读常量。
为了更加深入的了解各个内存空间之间的差异,下面给出一个例子:
int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{
static int staticVar = 1;
int localVar = 1;
int num1[10] = { 1, 2, 3, 4 };
char char2[] = "abcd";
const char* pChar3 = "abcd";
int* ptr1 = (int*)malloc(sizeof(int) * 4);
int* ptr2 = (int*)calloc(4, sizeof(int));
int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);
free(ptr1);
free(ptr3);
1. 对于变量,是一个全局变量,存储在数据段(静态区)。
2.对于变量,在定义的时候加上了关键字,存放在数据段(静态区)。
前两个变量虽然都存储在数据段(静态区),但是二者的链接属性并不相同,对于,可以在同一工程下的不同文件中共享使用。但是对于,只能在本文件中使用。
3. 对于变量staticVar,也是一个被关键字修饰的静态变量,同样存储在数据段(静态区),但是只能在本函数内部使用。
4.对于变量localVar,是函数内部的一个临时变量,存储在栈区
5.对于变量,是一个函数内部的数组,存储在栈区
6.对于变量,与变量同理,都是存储在
int* n1 = new int;
栈区上的
7.对于变量,与类似,存储在栈区上
8.对于变量,需要注意,在定义这个变量时,前面的修饰的并不是,而是。因此,也是存储在栈区,但是需要注意,,即字符串则是存储在代码段(常量区)。
9.对于,与类似,也是存储在栈区上的,但是,即后面利用开辟的空间是在堆区的。因此是存储在堆区上的
在C语言中,对于内存的管理通常是使用等函数完成的,在数据结构中,经常在创建一个关于数据结构的单个结点时使用。在C++中,引入了两个关键字来实现内存管理。其中,用来开辟内存空间,用来释放开辟的内存空间。对于这两个操作符的使用,下面将引入若干例子来说明:
1. 开辟一个类型为的空间:
int* n1 = new int;
2.一次性开辟个类型为的空间:
int* n1 = new int[10];
在C语言——动态内存管理:_编写内存管理代码-CSDN博客中提到,对于函数,只能开辟空间,并不能对开辟的空间进行初始化。而函数可以在开辟空间的同时将所开辟的空间初始化为。对于关键字,在上面的使用方法中,同样无法完成对于开辟空间的初始化。如果想初始化开辟的空间,例如将开辟的一个类型为的空间初始化为,则:
int* n2 = new int(0); //开辟一个类型为Int的空间,初始化为0
若需要同时初始化多个空间,则:
int* n3 = new int[10]{ 1,2,3 }; //开辟10个类型为int的空间,并初始化其中三个空间为1,2,3
不难发现,在上述代码中,开辟了个空间,但是仅对其中三个进行了初始化。对于其他的空间,默认初始化为
针对使用关键字开辟的内存空间,需要用关键字进行删除。对于单个空间的释放,代码如下:
int* n = new int; //开辟一个空间,类型为int
delete n; //释放上方开辟的空间
对于多个空间的释放,代码如下:
int* n1 = new int[10]; //开辟10个类型为int的空间
delete[]n1; //释放上面开辟的10个空间
前面说到,,这两个关键字是在中引入的,虽然语言中的函数已经满足了对于内存的开辟及释放。但是,这些函数针对于自定义类型并不能解决初始化的问题。因此,需要引入,来完成开辟自定义类型空间时的初始化问题。例如对于下方给出的自定义类型:
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << " A() " << " " << this << endl;
}
~A()
{
cout << " ~A() " << " " << this << endl;
}
private:
int _a;
};
按照上方开辟空间的方式,来开辟一个类型为的空间,代码为:
A* n4 = new A; //开辟一个类型为A的空间
而针对于如何对开辟的空间进行初始化。可以认为,在调用关键字开辟空间时,关键字进行了两步:第一步是开辟空间,第二步则是调用自定义类型的默认构造函数(全缺省、不需要传参就可以调用、编译器自动生成),对于上述代码中的类,由于其构造函数中的参数为全缺省,因此可以看作一个默认构造函数,但运行上方代码时,结果如下:
通过结果可以得知,在开辟空间时调用了自定义类型的默认构造函数。
如果想要显式调用,即不适用类中的构造函数的参数,方法如下:
A* n5 = new A(1);
上述开辟自定义类型空间只是开辟了一个,对于开辟多个自定义类型的空间,代码如下:
A* n6 = new A[5];
对于开辟多个空间的初始化,有下面几种方法:
A a1(1);
A a2(2);
A a3(3);
A* n7 = new A[3]{ a1,a2,a3 };
同时,可以借助匿名对象来达成初始化的效果:
A* n8 = new A[3]{A(1), A(2), A(3)};
此外,如果自定义类型的构造函数满足单参数这一条件
(注:单参数并不准确,具体条件可以在C++类与对象基础(8)-CSDN博客进行查看)
可以借助隐式类型转换来完成初始化,即:
A* n9 = new A[3]{ 4,5,6 };
对于自定义类型空间的释放,代码如下:
cout << "测试自定义类型空间的释放" << endl;
A* n9 = new A[3]{ 4,5,6 };
delete[]n9;
运行结果如下:
不难发现,代码在运行中调用了析构函数。原因是对于关键字,其动作过程也可以分为两步: 调用自定义类型的析构函数、释放空间。
由于个人能力有限,书中难免出现汉字拼写错误、代码意义解释错误、内容逻辑以及理解错误等不同类型的错误。首先感谢各位大佬能花掉自己宝贵的时间阅读此文章,愿大佬们斧正,发现错误可以通过私信联系,本人不胜感激。