3.5.3 指针和数组(2)
首先定义了两个名为pbeg和pend的整型指针,其中pbeg指向arr的第一个元素,pend指向arr尾元素的下一位置。while语句的条件部分通过比较pbeg和pend来确保可以安全地对pbeg解引用,如果pbeg确实指向了一个元素,将其解引用并检查元素值是否为负值。如果是,条件失效、退出循环;如果不是,将指针向前移动一位继续考查下一个元素。
一个指针如果指向了某种内置类型数组的尾元素的"下一位置",则其具备与vector的end函数返回的与迭代器类似的功能。特别要注意,尾后指针不能执行解引用和递增操作。
指针运算
指向数组元素的指针可以执行表3.6(第106页)和表3.7(第111页)列出的所有迭代器运算。这些运算,包括解引用、递增、比较、与整数相加、两个指针相减等,用在指针和用在迭代器上意义完全一致。
给(从)一个指针加上(减去)某整数值,结果仍是指针。新指针指向的元素与原来的指针相比前进了(后退了)该整数值个位置:
- constexpr size_t sz = 5;
- int arr[sz] = {1,2,3,4,5};
- int *ip = arr; // 等价于int *ip = &arr[0]
- int *ipip2 = ip + 4; // ip2指向arr的尾元素 arr[4]
ip加上4所得的结果仍是一个指针,该指针所指的元素与ip原来所指的元素相比前进了4个位置。
给指针加上一个整数,得到的新指针仍需指向同一数组的其他元素,或者指向同一数组的尾元素的下一位置:
- // 正确:arr转换成指向它首元素的指针;p指向arr尾元素的下一位置
- int *p = arr + sz; // 使用警告:不要解引用!
- int *p2 = arr + 10; // 错误:arr只有5个元素,p2的值未定义
当给arr加上sz时,编译器自动地将arr转换成指向数组arr中首元素的指针。执行加法后,指针从首元素开始向前移动了sz(这里是5)个位置,指向新位置的元素。也就是说,它指向了数组arr尾元素的下一位置。如果计算所得的指针超出了上述范围就将产生错误,而且这种错误编译器一般发现不了。
和迭代器一样,两个指针相减的结果是它们之间的距离。参与运算的两个指针必须指向同一个数组当中的元素:
- auto n = end(arr) - begin(arr); // n的值是5,也就是arr中元素的数量
两个指针相减的结果的类型是一种名为ptrdiff_t的标准库类型,和size_t一样,ptrdiff_t也是一种定义在cstddef头文件中的机器相关的类型。因为差值可能为负值,所以ptrdiff_t是一种带符号类型。
只要两个指针指向同一个数组的元素,或者指向该数组的尾元素的下一位置,就能利用关系运算符对其进行比较。例如,可以按照如下的方式遍历数组中的元素:
- int *b = arr, *e = arr + sz;
- while (b < e) {
- // 使用 *b
- ++b;
- }
如果两个指针分别指向不相关的对象,则不能比较它们:
- int i = 0, sz = 42;
- int *p = &i, *e = &sz;
- // 未定义的:p和e无关,因此比较毫无意义!
- while (p < e)
尽管作用可能不是特别明显,但必须说明的是,上述指针运算同样适用于空指针(参见2.3.2节,第53页)和所指对象并非数组的指针。在后一种情况下,两个指针必须指向同一个对象或该对象的下一位置。如果p是空指针,允许给p加上或减去一个值为0的整型常量表达式(参见2.4.4节,第65页)。两个空指针也允许彼此相减,结果当然是0。
解引用和指针运算的交互
指针加上一个整数所得的结果还是一个指针。假设结果指针指向了一个元素,则允许解引用该结果指针:
- int ia[] = {0,2,4,6,8}; // 含有5个整数的数组
- int last = *(ia + 4); // 正确:把last初始化成8,也就是ia[4]的值
表达式*(ia+4)计算ia前进4个元素后的新地址,解引用该结果指针的效果等价于表达式ia[4]。
回忆一下在3.4.1节(第109页)中介绍过如果表达式含有解引用运算符和点运算符,最好在必要的地方加上圆括号。类似的,此例中指针加法的圆括号也不可缺少。如果写成下面的形式:
- last = *ia + 4; // 正确: last = 4等价于ia[0] + 4
含义就与之前完全不同了,此时先解引用ia,然后给解引用的结果再加上4。4.1.2节(第136页)将对这一问题做进一步分析。
下标和指针
如前所述,在很多情况下使用数组的名字其实用的是一个指向数组首元素的指针。一个典型的例子是当对数组使用下标运算符时,编译器会自动执行上述转换操作。给定
- int ia[] = {0,2,4,6,8}; // 含有5个整数的数组