指针运算
指针与整数之间
在C ++中,您可以将运算符+和 - 应用于指针。 结果与某些方面熟悉的算术运算相似,但在其他方面不同。 将这些运算符应用于指针值的过程称为指针运算(pointer arithmetic.)。
指针算术由一个简单的规则定义。如果p是指向数组数组中的初始元素的指针,并且k是整数,则以下标识总是成立:
换句话说,如果将一个整数k添加到指针值,则结果是从原始指针地址开始的数组的索引k处的数组元素的地址。
为了说明这个规则如何适用,假设函数包含以下声明:
double list[3];
double *p;
这些变量中的每一个在该函数的框架中给出了空格。对于数组变量列表,编译器为数组中的三个元素分配空间,每个元素足够大以容纳一个double。对于p,编译器为指针分配足够的空间,这将用于保存double类型的一些左值的地址。如果栈从位置FFA0开始,则内存分配如下所示:
由于没有任何值分配给任何这些存储器位置,它们的初始内容是未定义的。假设你使用以下赋值语句在每个数组元素中存储值:
list[0] = 1.0;
list[1] = 1.1;
list[2] = 1.2;
并通过执行赋值语句将指针变量p初始化为数组的开头:
p = list;
或者你使用这样等价的语句:
p = &list[0];
赋值后,栈变为了这样:
在该图中,p现在指向数组列表中的初始地址。 如果向指针p添加一个整数k,则结果是与索引位置k处的数组元素相对应的地址。 例如,如果程序执行表达式:
p + 2;
此表达式的结果将是引用list [2]的新指针值。 因此,在上图中,其中p指向地址FFA0,p + 2指向在数组中出现两个元素的后元素的地址,该元素位于地址FFB0。
重要的是注意,指针加法不等同于传统的加法,因为计算必须考虑到基本类型的大小。 在此示例中,对于添加到指针值的每个单位,内部数值必须增加8以考虑到double类型需要8个字节的事实。
C ++编译器以类似的方式解释指针中的整数的减法。 表达方式:
p - k;
其中p是指针,k是整数.表达式用于计算位于当前由p表示的地址之前的k个元素的,数组元素的地址。 因此,如果将p设置为使用列表[1]的地址我们可以这样:
p = &list[1];
对应于p-1和p + 1的地址分别是list[0]和list[2]的地址.
指针与指针之间
注意:算术运算*,/和%对指针是没有用,不能与指针操作数一起使用。 此外,+和 - 与指针的使用是有限制的。在C中,你可以从指针中添加或减去整数,但是不能将两个指针加在一起。为指针定义的唯一其他算术运算是从另一个减去一个指针。
例如:
p1 - p2
被定义为返回当前值p1和p2之间的数组元素的数量。 例如,如果p1指向list [2],p2指向list [0],则表达式 p1 - p2 会返回 2 这样的一个整数值,因为当前指针值之间有两个元素。
自增和自减的指针
知道指针算术的规则使得可以理解C ++中最常见的惯用结构之一,即表达式:
*p++;
在这个表达式中,*运算符和++运算符竞争操作数p。 因为C ++中的一元运算符是以从右到左的顺序进行评估的,所以++优先于星号,所以编译器会将这个表达式解释为如下所示:
*(p++);
PS: 我们以后在涉及诸多代码的运算的情况下,尽量的用括号括出我们想先算的部分,避免不必要的混淆。
正如我们在前面学到的,后缀++操作符先增加p的值,然后返回p在增量操作之前的值。由于p是一个指针,所以++操作使用指针算术。因此,向p的值添加1将创建一个指向数组中下一个元素的指针。 例如,如果p最初指向arr [0],则增量操作将使其指向arr [1]。 因此,表达式:
*p++;
可以这样解释:
取消引用指针p,并将其当前指向的对象作为左值返回。作为++运算符的作用,增加p的值,以便如果原始值是数组中的元素,则p的新值指向该数组中的下一个元素。
作为如何使用* p ++构造的示例,以下代码使用基于指针的模型将元素添加到有效大小为n的数组中:
int sumArray(int *p, int n) {
int sum = 0;
for (int i = 0; i < n; i++) {
sum += *(p++);
}
return sum;
}
在for循环的每个循环中,表达式* p ++返回当前元素的值,然后p自增1,使其在下一个循环中被正确定位。
部分由于历史和习惯的原因,C ++程序员倾向于使用* p ++,其中数组符号使意图更清晰。
sumArray函数用以下基于数组的形式更容易读取:
int sumArray(int array[], int n) {
int sum = 0;
for (int i = 0; i < n; i++) {
sum += array[i];
}
return sum;
}
本文中的示例不使用指针算术,而是依赖于数组索引来提高程序示例的可读性。一般来说,你应该在自己的代码中采用相同的规则。但是,如果你查看现有的代码,你将一定会遇到* p ++(甚至可能更复杂的指针运算应用程序),你需要知道该构造是什么意思。 此外,STL使用相同的句法模式来实现我们之前介绍的迭代器。
记住* p ++是检索数组的当前元素, 然后自增指向下一个元素。表达式* p ++返回当前元素的值,然后p自增1,使其在下一个循环中被正确定位。