C++ Primer Plus 学习笔记(复合类型)(三)

1. 指针和自由存储空间

指针是一个变量,其存储的是值的地址,而不是值本身。

对于常规变量,只需要对变量应用地址运算符(&)就可以获得它的位置。(*)运算符被称为间接值或解除引用运算符,将其应用于指针,可以得到该地址处存储的值。

假设 manly 是一个指针,则 manly 表示的是一个地址,而 *manly 表示存储在该地址处的值。

示例代码:

int updates = 6;
int *p_updates;
p_updates = &updates;
cout << "Values: updates = " << updates;
cout << ", *p_updates = " << *p_updates << endl;
cout << "Address: &updates = " << &updates;
cout << ", p_updates = " << p_updates << endl;
*p_updates = *p_updates + 1;
cout << "Now updates = " << updates << endl;

运行结果:

Values: updates = 6, *p_updates = 6
Address: &updates = 0x7ffeefbff4bc, p_updates = 0x7ffeefbff4bc
Now updates = 7

int 变量 updates 和指针变量 p_updates 相当于一枚硬币的两面,updates 表示值,并使用 & 运算符来获得地址;而变量 p_updates 表示地址,并使用 * 运算符来获得值。由于 p_updates 指向 updates,因此 *p_updates 和updates 完全等价。

1. 1 指针的声明

指针声明必须指定指针指向的数据类型,如:

int * p_updates;

* 运算符两边的空格可选的,传统上,C 程序员使用:int *ptr;,强调 ptr 是一个 int 类型的值。而很多 C++ 程序员使用:int* ptr;。强调 int 是一种指向 int 的指针。
但是int* p1, p2; 声明创建一个指针(p1)和一个 int 变量(p2)。

1. 2 指针的初始化

可以在声明语句中初始化指针,这种情况下,被初始化的是指针,而不是它指向的值。

int higgens = 5;
int * pt = &higgens;

注意:一定要在对指针应用解除引用运算符(*)之前,将指针初始化为一个确定的、适当的地址。

1. 3 指针和数字

指针不是整型,虽然计算机通常把地址当作整数来处理,但指针与整数时截然不同的类型。不能简单地将整数赋给指针:

int * pt;
pt = 0xB8000000;    // type mismatch

1. 4 使用 new 来分配内存

在运行阶段为一个 int 值分配未命名的内存,并使用指针来访问这个值,可以使用 C++ 的new 运算符:

int * pn = new int;

new 运算符根据类型来确定需要多少字节的内存,然后找到这样的内存,并返回其地址。接下来,降低至赋给 pn,pn 是被声明为指向 int 的指针。现在 pn 是地址,而 *pn 是存储在那里的值。

为一个数据对象(可以是结构,也可以是基本类型)获得并制定分配内存的通用格式如下:

typeName * pointer_name = new typeName;

1. 5 使用 delete 释放内存

当需要内存时,可以使用 new 来请求。当使用完内存后,可以使用 delete 运算符将内存还给内存池。使用 delete 时,后面要加上指向内存块的指针

int * ps = new int;
...
delete ps;

这将会释放 ps 指向的内存,但不会删除指针 ps 本身。一定要配对地使用 new 和 delete,否则将发生内存泄漏,即,被分配的内存再也无法使用。

不要尝试释放已经释放的内存块,这样做的结果将是不确定的,意味着什么情况都可能发生。

注意:只能用 delete 来释放使用 new 分配的内存,然而,对空指针使用 delete 是安全的。

使用 delete 的关键在于,将它用于 new 分配的内存。这并不意味着要使用用于 new 的指针,而是用于 new 的地址。对于下面的代码:

int * ps = new int;
int * pq = ps;
delete pq;

一般来说,不要创建两个指向同一个内存块的指针,因为这将增加错误地删除同一个内存块两次的可能性。

1. 6 使用 new 来创建动态数组

创建方法:

int * psome = new int [10];

new 返回第一个元素的地址。在上面的例子中,该地址被赋给指针 psome。访问其中的元素的方法:对于第 1 个元素,可以使用 psome[0] ,*psome;对于第 2 个元素,可以使用 psome[1]。

对于 new创建的数组,应使用另一种格式的 delete 来释放:

delete [] psome;

使用 new 和 delete 时,应遵守如下规则:

  • 不要使用 delete 来释放不是 new 分配的内存。
  • 不要使用 delete 释放同一个内存块两次。
  • 如果使用 new [] 为数组分配内存,则应使用 delete [] 来释放。
  • 如果使用 new 为一个实体分配内存,则应使用 delete 来释放。
  • 对空指针应用 delete 是安全的。

为数组分配内存的通用格式如下:

type_name * pointer_name = new type_name [num_elements];

2. 指针、数组和指针算术

C++ 将数组名解释为数组第 1 个元素的地址,对于下面的语句将 pw 声明为指向 double 类型的指针,然后将它初始化为 wages——wages 数组中第 1 个元素的地址:

double * pw = wages;

对于 pw 和 *pw,前者是地址,后者是存储在该地址中的值。由于 pw 指向第 1 个元素,因此 *pw 现实的值为第 1 个元素的值。将 pw 加 1,数字地址将增加 8,是的 pw 的值为第 2 个元素的地址。

和所有数组一样,wages 也存在下面的等式:

wages = &wages[0] = address of first element of array;

通常,使用数组表示法时,C++ 都执行如下转换:

arrayname[i] become *(arrayname + i)

如果使用的是指针,而不是数组名,则 C++ 也将指向同样的转换:

pointername[i] becomes *(pointername + i)
使用 new 创建动态结构

创建方法如下:

inflatable * ps = new inflatable;

对于访问成员,创建动态结构时,不能将成员运算符据点用于结构名,因为这种结构没有名称,只是知道它的地址,因此,需要使用箭头成员运算符(->)。如果 ps 指向一个 inflatable 结构,则 ps->price 是被指向的结构的 price 成员。

另一种访问结构成员的方法是,如果 ps 是指向结构的指针,则 *ps 就是被指向的值——结构本身,由于 *ps 是一个结构,因此 (*ps).price 是该结构的 price 成员。C++ 的运算符优先规则要求使用括号。

3. 数组的替代品

3. 1 模板类 vector

vector 是使用 new 创建动态数组的替代品,vector 确实使用 new 和 delete 来管理内存,但这种工作是自动完成的。

使用 vector 对象,必须包含头文件 vector;vector 包含在名称空间 std 中。模板使用不同的语法来指出它存储的数据类型。vector 类使用不同的语法来指定元素数。

示例:

#include <vector>
using namespace std;
vector<int> vi;         // create a zero-size array of int
int n;
cin >> n;
vector<double> vd(n);   //  create an array of n doubles

vector对象在插入或添加值时自动调整长度。

通用的声明方法是:

vector<typeName> vt(n_elem);

3. 2 模板类 array

vector 的效率稍低,如果需要长度固定的数组,使用数组是更佳的选择,但不那么方便和安全。

array 对象的创建语法如下:

#include <array>
using namespace std;
array<int, 5> ai;
array<double, 4> ad = {1.2, 2.1, 3.43, 4.3};

通用语法为:

array<typeName, n_elem> arr;

3. 3比较数组、vector 对象和 array 对象

  • 无论数组、vector 对象还是 array 对象,都可以使用标准数组表示法来访问各个元素。
  • array 对象和数组存储在相同的内存区域(即栈)中,而 vector 对象存储咋另一个区域(自由存储区或堆)中。
  • 可以将一个 array 对象赋给另一个 array 对象;而对于数组,必须逐元素复制数据。
  • 数组不会检查超界错误,可以访问位置位于数组之外的内存。因此数组的行为是不安全的。
  • vector 和 array 对象可以禁止超界现象,即使用 at() 函数,可以在运行期间补货非法索引,而程序默认将中断。
  • 可以使用函数 begin() 和 end(),用来确定边界,以免无意间超界。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值