c++从零开始---复合类型之指针 new 运算符

指针真正地用武之地在于,在运行阶段分配未命名的内存以存储值。在这种情况下,只能通过指针来访问内存。c++ 提供了一种方法—new 运算符。

1、如何使用 new 运算符

首先,程序员需要告诉 new,需要为哪种数据类型分配内存,然后,new 将找到一块长度正确的内存块,并返回该内存块的地址。之后程序员再将改地址赋给一个指针。示例如下:

int *pn = new int;

new int 告诉程序,需要存储 int 的内存,new 运算符根据类型来确定多少内存的字节。然后,它找到这样的内存并返回地址。接下来将地址赋给 pn,pn 是被声明为指向 int 的指针。在这种情况下,因为 pn 指向的内存没有名称,只能通过指针对其进行访问。


为一个数据对象(可以是结构,也可以是基本类型)获得并指定分配内存的通用格式如下:
*typeName pointer_name = new typeNmae;
需要在两个地方指定数据类型:用来指定需要什么样的内存和用来声明合适的指针。以下程序演示了如何使用 new:

#include <iostream>
int main()
{
	using namespace std;
	// new int 类型
	int a = 100;
	int* pt = new int;
	*pt = 100;
	cout << "a value: " << a << endl;
	cout << "a location: " << &a << endl;
	cout << "new int value: " << *pt << endl;
	cout << "new int location: " << pt << endl;
	// new double 类型
	double b = 100.1;
	double* pd = new double;
	*pd = 100.1;
	cout << "b value: " << b << endl;
	cout << "b location: " << &b << endl;
	cout << "new double value: " << *pd << endl;
	cout << "new double location: " << pd << endl;
	// 观察 int 和 double 的地址大小
	cout << "sizeof pt: " << sizeof(pt) << endl;
	cout << "sizeof pd: " << sizeof(pd) << endl;
	return 0;
}

程序中使用 new 分配了内存后,使用 *pt 的方式访问数据,这样就可以像使用变量那样使用 *pt 和 *pd 了。

2、使用 delete 释放内存

delete 运算符使得在使用完内存后,能够将其归还给内存池。归还的内存池可供程序的其它部分使用。

使用 delete 时,后面要加上指向内存块的指针(这些内存块必须是由 new 分配的),示例程序如下:

int *pr = new int; // 分配内存
...  // 程序段,使用该内存
delete pr; // 释放内存

使用 delete 将释放 pr 指向的内存,但不会删除指针 pr 本身。之后依然可以将 pr 指向另一个新分配的内存块。

使用 new 创建了空的内存块,用 pr 指向了内存块所在的地址,而使用 delete 就是把这个内存块给释放掉,使得其可以存放供给其它程序使用。

注意:
1、delete 只能删除使用 new 创建的内存块,即 new 和 delete 要配对使用,否则将发生内存泄漏的情况,那么被分配的内存再也无法使用了;
2、不要释放已经释放的内存块,否则结果无法预测;

int *pr = new int; // valid
delete pr; // valid
delete pr; // invalid,the second time to use delete
int a = 10;
int *po = &a; //valid
delete pi;  // invalid,内存不是通过 new 分配的

实际上,delete 是作用于 new 分配的内存,而非作用于 new 创建的指针。对内存进行了 delete 操作后,被操作的指针也会失去地址值。此时的指针属于未初始化的状态。

int *pr = new int; // 创建一个指针 pr 指向 new 分配的地址
int *ps = pr;  // 创建一个指针 ps,与 pr 共同指向 new 分配的地址
delete ps;  // 删除了 new 分配的内存块,但是 delete 并非直接作用于 pr

3、使用 new 来创建动态数组

假设要编写一个程序,它是否需要数组取决于运行时用户提供的信息,如果通过声明来创立数组,则在程序被编译时将为它分配内存空间。不论程序最终是否使用数组,数组都在那里,它都占用了内存。
1、在编译时给数组分配内存被称为静态联编,意味着数组是在编译时加入到程序中的。
/2、使用 new 时,如果在运行阶段需要数组,则创建它;如果不需要,则不创建。还可以在程序运行时选择数组的长度,这被称为动态联编,意味着数组是在程序运行时创建的。这种数组叫做动态数组
使用静态联编时,必须在编写程序时指定数组的长度;使用动态联编时,程序将运行时确定数组的长度。

3.1 使用 new 创建动态数组

创建一个包含 10 个 int 元素的数组:

int *pr = new int [10];

new 运算符返回第一个元素的地址,在这里,该地址被赋给指针 pr。
对于使用 new 创建的数组,用 delete 释放内存时,与创建简单内存空间有所不同:

delete [] pr;

使用方括号是为了告诉 delete,应该释放整个数组的内存,而不仅仅是指针 pr 指向的内存。
注意:释放简单数据类型和数组的方式是不同的,前者没有方括号,后者有方括号。

3.2 使用动态数组

实际上,可以将指针当作数组名使用,即可操作数组。

int *pr =new int[10];

对于该数组的第一个元素,可以使用 pr[0],而非 *pr,第二个元素,使用 pr[1],以此类推。这是 c++ 内部的用法,数组和指针等价是其优点之一。
使用方法参考以下程序

#include <iostream>
int main()
{
	using namespace std;
	double* p3 = new double[3]; // 创建 3 个 double 
	p3[0] = 0.1;
	p3[1] = 0.2;
	p3[2] = 0.3;
	cout << "p3[1] is: " << p3[1] << endl;
	p3 = p3 + 1; // 此行参考下列具体阐述
	cout << "now p3[0] is: " << p3[0] << " and "
		<< "p3[1] is: " << p3[1];
	p3 = p3 - 1;
	delete [] p3;
	return 0;
}

p3 = p3 + 1;表达了两层意思:
1、虽然程序里将 p3 这一个指针当作数组名进行操作,但是实际上数组名是不能进行修改的。p3 具有数组的特性,但是也具有指针的变量特性,它是一个变量,因此可以对其进行修改;
2、从指针的角度考虑其加 1 的后果:p3 本身指示数组的第一个元素的地址,在其基础上加 1,那么可以理解为 p3 指示到了数组的第二个元素的地址,因此输出 p3[0]后得到的是原 p3[1]的值。

4、使用 new 创建动态结构

将 new 用于结构由两步组成:创建结构和访问其成员

创建动态结构时,不能将成员运算符(.)用于结构名,因为这种结构没有名称,只知道其地址。c++ 提供了专门的解决方案:箭头成员运算符(->),由连字符和大于号组成。这种运算符用于指向结构的指针。此外,由于 *pr 表示结构本身,那么也可以将其作为结构名使用:

#include <iostream>
struct inflatable //创建结构
{
	char name[20];
	float volume;
	double price;
};
int main()
{
	using namespace std;
	inflatable* ps = new inflatable; // 创建指向结构的指针
	cout << "Enter name of inflatable item: ";
	cin.get(ps->name, 20); // 指针使用箭头成员运算符
	cout << "Enter volume in cubic feet: ";
	cin >> (*ps).volume; // 也可以将 *ps 看成结构名
	cout << "Enter price: $";
	cin >> ps->price;
	cout << "Name: " << (*ps).name << endl;
	cout << "Volume: " << ps->volume << endl;
	cout << "price: $" << ps->price << endl;
	delete ps;
	return 0;
}

5、new 和 delete 实例理解

#include <iostream>
#include <cstring>
using namespace std;
char* getname(void);
int main()
{
	char* name;

	name = getname();
	cout << name << " at " << (int*)name << endl;
	delete[] name;

	name = getname();
	cout << name << " at " << (int*)name << endl;
	delete[] name;
	return 0;
}

char* getname()
{
	char temp[80];
	cout << "Enter last name: ";
	cin >> temp;
	char* pn = new char[strlen(temp) + 1];
	strcpy_s(pn,strlen(temp)+1, temp);
	return pn;
} 

程序说明:
对于程序中的 getname()函数,它使用 cin 将输入的单词放入 temp 中,然后使用 new 分配内存,以存储该单词。程序需要 strlen(temp)+1个字符(包括空字符)来存储该字符串,因此将这个值提供给 new。获得空间后,getname() 使用标准函数 strcpy_s() 将 temp 中的字符复制到新的内存块中。最后返回 pn。
在 main() 函数中,name 得到了 getname() 的返回值 pn。在使用了字符串之后使用 delete 来释放 name 的内存块。
每一次的输入调用 getname()函数来分配内存,使得所分配的内存刚好能够存储字符串,相比于静态联编,这样能够节省大量的内存。


6、学完本章存在的疑问

  1. 在介绍动态数组时,书上说的是在程序运行时确定数组的长度,但是 int *pr = new int[10]这样的语句,new
    运算符已经分配了 10 个内存块给 10 个元素,那就是在程序运行前已经分配了内存,和静态联编好像没有差异。
  2. 此外,书上解释使用 new 时,需要数组时则创建,不需要则不创建?但是同样的在程序编译时 new 不是已经分配了内存了吗?

解答:(自己的理解,以后理解更深入再修改)

  1. 在编译时,程序并未给 new 分配内存,在程序运行到 new
    时才实时分配。数组是在编译时直接分配了内存,并且数组的长度一定已经确定了,它的长度的值只能由常量作参数;new
    使得数组的长度可以随着情况进行确定,比如输入字符长度。
  2. 由于 new 的长度是根据实际情况确定的,如果给 new 的参数为 0,那么 new 实际上不需要分配内存。
  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值