使用指针形参
继续上一篇,继续《C Primer Plus》第10章的学习。
上一篇统计数组中各元素的和,用了两种方法,一种是数组,一种是指针。
sum()函数使用一个指针形参标识数组的开始,用一个整数形参表明待处理数组的元素个数。
int sum(int * ar, int n)
int sum(int ar[], int n)
我们还有一种方法,是传递两个指针,第1个指针指明数组的开始处,第2个指针表示数组的结束处。
运行结果:
之前的sum()函数使用第2个参数,即元素个数作为其中for循环的一部分,用来求和。
for(i = 0; i<n; i++)
total += *(ar + i);
这个sump()函数中的循环体采用while循环:
while (start < end)
{
total += *start; // add value to total
start++; // advance pointer to next element
}
这里就是每次取当前指针指向的值加入到和中,并将指针移动一个元素,直到末尾。
这个循环体也可以精简为一行:
while (start < end)
total += *start++;
说明:
在C语言中,ar[i]和*(ar+i)是等价的,无论ar是数组名还是指针变量,这两个表达式都可以。
但是,只有当ar是指针变量时,才能使用ar++这样的表达式。
As far as C goes, the two expressions ar[i] and *(ar+i) are equivalent in meaning. Both work if ar is the name of an array, and both work if ar is a pointer variable. However, using an expression such as ar++ only works if ar is a pointer variable.
指针的递增顺序
*p++
一元运算符*和++的优先级相同,但结合律是从右往左。
*p++等同于*(p++),是先使用指针指向的值,然后将指针递增。
Use the value pointed to, then increment the pointer.
如果是*++p,就是先进行指针的递增,然后使用其指向的值。
*++p, the order would be increment the pointer, then use the value pointed to.
而(*p)++则是使用该指针指向的值,然后将该值递增。也就是指针的指向不变,其中的值会加1。
(*p)++, however, it would use the value of p and then increment the value, not the pointer. That would leave the pointer pointing to the same element, but the element would contain a new number.
看一下示例:
可以看到*p1++还是100,而*++p2就是200, 而且这时p1和p2都指向了下一个存储地址,0x601040,但是p3的指向没有变,(*p3)++只是让p3指向的值自增1。
指针的运算
主要是指针+n,或者指针-n,会移动相应的n*数据类型大小
可以看到指针ptr指向该数组的首位,值是0x7f……170,ptr+4,指针就会+4*4, 16进制加16就是0x7f……180。
指针和多维数组
以一个简单的二维数组为例:
int zippo[4][2]; /* an array of arrays of ints */
Then zippo, being the name of an array, is the address of the first element of the array. In this case, the first element of zippo is itself an array of two ints, so zippo is the address of an array of two ints. Let’s analyze that further in terms of pointer properties:
Zippo,作为该数组的名称,是该数组首元素的地址。Zippo的首元素实际是包含两个int元素的数组,所以zippo是一个包含两个int元素的数组的地址。下面我们从指针的属性来进一步分析。
■ Because zippo is the address of the array’s first element, zippo has the same value as &zippo[0]. Next, zippo[0] is itself an array of two integers, so zippo[0] has the same value as &zippo[0][0], the address of its first element, an int. In short, zippo[0] is the address of an int-sized object, and zippo is the address of a two- int-sized object.
因为zippo是数组首元素的地址,所以zippo的值和&zippo[0]的值相同。Zippo[0]本身是一个内含两个整数的数组,所以zippo[0]的值和它首元素的地址&zippo[0][0]相同。简而言之,zippo[0]是一个占用一个int大小的对象的地址,而zippo是一个占用两个int大小的对象的地址。
■ Adding 1 to a pointer or address yields a value larger by the size of the referred-to object. In this respect, zippo and zippo[0] differ, because zippo refers to an object two int s in size, and zippo[0] refers to an object one int in size. Therefore, zippo + 1 has a different value from zippo[0] + 1 .
给指针或地址加1,其值会增加对应类型大小的数值。这里,zippo和zippo[0]是不同的,因为zippo指向的对象占用了两个int大小,而zippo[0]指向的对象占用一个int大小。
程序实例:
运行结果:
通过程序实例可以看出:
zippo的值是:0x7f……830,zippo + 1是0x7f……838,加了两个int的大小,也就是加了8。
而zippo[0]+1是0x7f……834,加了一个int的大小。
对应指针:
zippo[0][0]的值也就是**zippo
zippo[0][1]的值也就是 *(*zippo+1)
zippo[2][1]的值也就是*(*(zippo+2)+1)
指向多维数组的指针:
如何声明一个指针变量pz,指向一个二维数组,如zippo?
pz必须指向一个内含两个int类型值的数组,其声明如下:
Int (* pz)[2]
如下示例,重点关注 (* p2) [2]
运行结果:
仔细分析一下:
程序中第5、6、7行分别针对二维数组zippo,一维数组zippo[0]和整型zippo[0][0]定义了相对应的3个指针。下面的8、9、10行则进行了相应的赋值。
第11行的运行结果,p1、p2、p3的值都相同,这是由于二维数组zippo的基地址就是其首元素zippo[0]的基地址,而zippo[0]的基地址也是其首元素zippo[0][0]的基地址。
接下来,三个指针都+1的结果完全不同,就能看出它们的区别了。
p1+1就是+4*8,也就是加了32,对于16进制,就是由0x……550,变为了0x……570,
而p2+1,因为p2的一个元素是两个整数的数组,所以增加了8,也就是变为了0x……558。
p3+1则是增加一个整数的大小,就是增加4,变为了0x……554。
后面的运算主要是针对一般定义二维数组的(*p2)[n] 进行的。
等同于上一个程序中的zippo
P2[2][1]也就等于*(*(p2+2) +1)