指针管理运行阶段的内存分配

发布时间:2023年12月26日

一、new分配内存

编程时可以将指针初始化为一个变量的地址;而变量是在编译时分配的有名称的内存,所以指针只是为可以通过名称直接访问的内存提供了一个别名。即也可以通过指针完成内存的访问。
但指针真正的用武之地在于,在运行阶段分配未命名的内存以存储值,此时只能通过指针访问内存。在C语言中可以使用malloc()库函数来分配内存,在C++中也可以继续这么做,但是使用new运算符是更好的做法。
为一个数据对象获得并分配内存的通用格式如下:

tyepName * pointer_name = new typeName;

需要在两个地方指定数据类型,用来指定需要什么样的内存和用来声明合适的指针。

int * pn = new int;

在上例中,new int 告诉程序需要适合存储int的内存,new运算符根据类型确定需要多少字节的内存,然后它找到这样的内存,并返回其地址,将地址赋值给pn,pn是被声明为指向int的地址。注意pn是地址,而*pn是存储在那里的值。

int higgens;
int * pt = &higgens;

在上例中,相比于第一个示例,除了都可以通过指针访问内存以外,也可以通过变量名称higgens来访问该int地址。

#include <iostream>
int main()
{
	using namespace std;
	int nights = 1001;
	int * pt1 = & nights;
	
	int * pt = new int;
	*pt = 1002;

	double * pd = new double;
	*pd = 100.1;

	int length_pt = sizeof(pt1);
	int length_pd = sizeof(pd);
	int length_pt_value = sizeof(*pt);
	int length_pd_value = sizeof(*pd);
}

上例中,程序使用new分别为int类型和double类型的数据对象分配内存,这是程序在运行时候进行的,指针指向数据对象,可以像使用变量那样使用*pt和*pd,比如赋值或取值。并且指向int的指针和指向double的指针的长度相同,它们都只是地址,但是在程序中声明了指针的类型,因此程序知道*pd是8个字节的double值,*pt是4个字节的int值。

注意:
1、new分配的内存块通常与常规变量声明分配的内存块不同,常规变量的值都存储在称为栈(stack)的内存区域中,而new从被称为堆(heap)或自由存储区的内存区域分配内存。
2、因为异常等原因new运算符或函数没有分配成功,则返回一个空指针(null pointer)

二、delete释放内存

当需要内存时,可以使用new运算符请求,另一方面当使用完内存后,能够将其返还给内存系统,此时可以使用delete运算符。归还或释放的内存可以供程序的其他部分使用。使用delete时,后面要加上指向内存块的指针(这些内存块最初是用new分配的)。

int * ps = new int; // allocate memory with new;
...					// use the memory
...
delete ps;			// free memory with delete when done

在上例中,通过new申请一个指向int类型的未命名的内存,并通过ps这个指针访问该内存,delete运算符将释放ps指向的未命名的内存,但不会删除指针ps本身,后面可以将ps重新指向另一个新分配的内存块或NULL。

在这里需要注意:
1、一定要配对的使用new和delete,否则将会发生内存泄漏,也就是说被分配的内存再也无法使用,内存泄漏严重时,程序会因为不断寻找更多内存而被终止执行。
2、不要尝试释放已经被释放的内存块,这将会导致不确定的结果。
3、只能用delete来释放使用new分配的内存,不能使用delete来释放声明变量所获得的内存。
4、对空指针使用delete是安全的。

三、动态数组的创建与使用

当程序中是否需要创建数组,以及数组的长度取决于运行时的信息时,则可以通过new和指针完成。
静态联编:在编译时给数组分配内存被称为静态联编(static binding),这意味着数组是在编译时加入到程序中的,编写程序时已经确定了数组的长度。
动态联编:如果在运行阶段需要数组则创建它,如果不需要则不创建,数组的长度也可以在程序运行时确定,这意味着数组是在程序运行时创建的。此时也称为动态数组。

1、使用new创建动态数组

告诉new运算符,数组的元素类型和元素数目即可创建动态数组,必须在类型名后加上方括号,其中包括元素数目。

tyepName * pointer_name = new typeName [num_elements]...
delete [] pointer_name;

使用new运算符可以确保内存块足以存储num_elements个类型为type_name的元素,而pointer_name指向第一个元素。

int * psome = new int [10];
...
...
delete [] psome;

new运算符返回第一个元素的地址,在上例中该地址被赋值给指针psome。即psome是指向一个int(数组的第一个元素)的指针
程序使用完new分配的内存块后,使用delete释放它们,方括号[]告诉程序应释放整个数组,而不仅仅是指针指向的元素。

在这里要注意:
1、如果使用new时不带方括号,则使用delete时也不应该带方括号。
2、如果使用new时带方括号,则使用delete时也应带方括号。
3、不要使用delete来释放不是new分配的内存。
4、不要使用delete释放同一个内存块两次。

2、使用动态数组

int * psome = new int [10];
...
...
delete [] psome;

在上例中,psome指向包含10个int值的内存块中的第一个元素。使用直接使用*psome为第一个元素的值。
动态数组元素的访问,则可以将指针当做数组名使用即可,即第一个数组元素可以使用psome[0],第二个元素为psome[1],以此类推。在C++好C语言中,都使用指针来处理数组,此时数组和指针基本等价。

#include <iostream>
int main()
{
	using namespace std;
	int * pt = new int [3];
	pt[0] = 0;
	pt[1] = 1;
	pt[2] = 2;
	cout << "pt[0] value is " << pt[0];
	
	pt = pt + 1;
	cout << "pt[0] value is " << pt[0];

	pt = pt - 1;
	cout << "pt[0] value is " << pt[0];
	
	delete [] pt ;
}

上例中,可以将指针名当做数组来用,pt[0]为数组第一个元素,以此类推。
对于常规的数组名,其值无法修改,固定为第一个元素的地址,但是指针为变量,可以修改它的值。
一开始pt[0]的值为0,在将pt加1后,pt[0]的值变为数组第二个元素的值,因为将pt加1导致它指向数组的第二个元素而不再是第一个,再将pt减1后,又指向原来的值,这样程序可以给delete提供正确的起始地址,以全部释放分配内存。

文章来源:https://blog.csdn.net/jzh18755117156/article/details/135210533
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。