指针真正地用武之地在于,在运行阶段分配未命名的内存以存储值。在这种情况下,只能通过指针来访问内存。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;
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;
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;
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;
delete pr;
delete pr;
int a = 10;
int *po = &a;
delete pi;
实际上,delete 是作用于 new 分配的内存,而非作用于 new 创建的指针。对内存进行了 delete 操作后,被操作的指针也会失去地址值。此时的指针属于未初始化的状态。
int *pr = new int;
int *ps = pr;
delete ps;
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];
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;
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、学完本章存在的疑问
- 在介绍动态数组时,书上说的是在程序运行时确定数组的长度,但是
int *pr = new int[10] 这样的语句,new 运算符已经分配了 10 个内存块给 10 个元素,那就是在程序运行前已经分配了内存,和静态联编好像没有差异。 - 此外,书上解释使用 new 时,需要数组时则创建,不需要则不创建?但是同样的在程序编译时 new 不是已经分配了内存了吗?
解答:(自己的理解,以后理解更深入再修改)
- 在编译时,程序并未给 new 分配内存,在程序运行到 new
时才实时分配。数组是在编译时直接分配了内存,并且数组的长度一定已经确定了,它的长度的值只能由常量作参数;new 使得数组的长度可以随着情况进行确定,比如输入字符长度。 - 由于 new 的长度是根据实际情况确定的,如果给 new 的参数为 0,那么 new 实际上不需要分配内存。
|