本节书摘来自异步社区《指针的编程艺术(第二版)》一书中的第3章,第3.6节,作者 蔡明志,更多章节内容可以访问云栖社区“异步社区”公众号查看
3.6 多重指针
指针的编程艺术(第二版)
多重指针(multiple pointer){XE "多重指標(multiple pointer)"},指的是在一个语句中有2个或以上的*。我们以范例starstarPointer-3来说明。
范例starstarPointer-3
/* starstarPointer-3.c */
#include <stdio.h>
#include <conio.h>
int main()
{
int i[5]={10, 20, 30, 40, 50};
int *ptr[] = {i, i+1, i+2, i+3, i+4};
int **p2 = ptr;
int k;
for(k=0; k<5; k++)
printf("*ptr[%d]=%d\n", k, *ptr[k]);
printf("\n");
for(k=0; k<5; k++)
printf("**(p2+%d) = %d\n", k, **(p2+k));
getch();
return 0;
}
输出结果
![e5fc15dbd1cac3a2dddd1480c4059a17ae43014f](https://yqfile.alicdn.com/e5fc15dbd1cac3a2dddd1480c4059a17ae43014f.png?x-oss-process=image/resize,w_1400/format,webp)
程序中
int *ptr[] = {i, i+1, i+2, i+3, i+4};```
表示ptr是一个数组,这个数组有5个元素,每个元素都是指向int的指针,也可以说ptr与一个数组指针p2需经过两次的间接访问才能得到值,用图表示如下。
<div style="text-align: center">
<img src="https://yqfile.alicdn.com/b9766579fc4db4f637cd296fb5fcc46bab42b8f0.png" >
</div>
从图中可以很容易地得到答案。
比较复杂的是当*和++或--混合使用时就必须非常小心,分析++或--是作用于地址还是值。请参阅范例starstarPointer-5.c。
范例starstarPointer-5.c
/ starstarPointer-5.c /
include
include
int main()
{
int i[] = {10, 20, 30, 40, 50};
int *pa[] = {i, i+2, i+1, i+4, i+3};
int **p2 = pa;
printf("Initial p2 = %dn", p2);
p2++;
printf("After p2++, the p2 = %dn", p2);
++*p2;
printf("After ++*p2, the p2 = %dn", p2);
**p2++;
printf("After p2++, the p2 = %dn", **p2);
++**p2;
printf("After ++p2, the p2 = %dn", **p2);
system("PAUSE");
return 0;
}
输出结果
<div style="text-align: center">
<img src="https://yqfile.alicdn.com/5b46091d54ba500a713e88dc5d5b06b3d958e92c.png" >
</div>
我们用图来辅助说明以上的变量定义。i数组定义如下
int i[] = {10, 20, 30, 40, 50};`
表示i是5个元素的数组,其示意图如下所示。
![3cc38e5c332a0288bd794707bfa3cb92813b813a](https://yqfile.alicdn.com/3cc38e5c332a0288bd794707bfa3cb92813b813a.png?x-oss-process=image/resize,w_1400/format,webp)
而 pa 数组定义如下
int *pa[] = {i, i+2, i+1, i+4, i+3};```
表示pa是一个数组指针,其中pa[0]、pa[1]、…、pa[4],分别指向i数组的某一元素的地址,其示意图如下所示。
<div style="text-align: center">
<img src="https://yqfile.alicdn.com/a4c9bf0f3c8dd090bb9c5f06bf9fa3e7f5c9fbc7.png" >
</div>
而p2变量定义如下。
int **p2 = pa;`
表示p2为一个指向指针的指针,初始值为pa(pa[0]的地址),注意,p2是指针变量。整个程序的示意图如下所示。
![752332d4f71d6645f562501e2aa6743709975b2e](https://yqfile.alicdn.com/752332d4f71d6645f562501e2aa6743709975b2e.png?x-oss-process=image/resize,w_1400/format,webp)
我们来剖析程序中的问题。注意图形中的粗线条。
1. p2++;
由于p2++会更新p2的值,而其值原来是pa,无论是前置+还是后继+,p2都会往前一个元素,此时的p2是pa+1,所以 **p2 等于 30。
![07597036b16de8da6d1a07d50602fdba7c869afe](https://yqfile.alicdn.com/07597036b16de8da6d1a07d50602fdba7c869afe.png?x-oss-process=image/resize,w_1400/format,webp)
2. ++*p2;
由于++和具有相同的运算优先级,而其结合性由右至左,因此,++就是针对p2所指向的地址加1。
![d3c0ebcaea12fbe747b86a5a40d0488209df715c](https://yqfile.alicdn.com/d3c0ebcaea12fbe747b86a5a40d0488209df715c.png?x-oss-process=image/resize,w_1400/format,webp)
注意p2是pa[1],pa[1]的值原先是指向i+2,经过++p2之后,p2的地址将更新为i+3,所以*p2的值是40。
3. **p2++;
由于和++的运算优先级相同,但其结合性是由右至左,因此p2++实际上是(p2++);虽然p2++先做,不过此处的++是后置加,因此会先将它搁在一边,处理完*之后,再执行加1,所以就这条语句而言,只是对p2的地址指向下一个而已,示意图如下。
![29a6bb94ad4375b1c036abd218f9b1079240ef80](https://yqfile.alicdn.com/29a6bb94ad4375b1c036abd218f9b1079240ef80.png?x-oss-process=image/resize,w_1400/format,webp)
原先p2指向pa+1,所以p2++则是指向pa+2,**p2也就等于20。
4. ++**p2;
这条语句的++是针对p2加1,p2的结果是一个值,获取后将它加1,便是最后的答案。
![fb3d4a491be1c8e4cb6cfe6db5e9a04ea98a2cf2](https://yqfile.alicdn.com/fb3d4a491be1c8e4cb6cfe6db5e9a04ea98a2cf2.png?x-oss-process=image/resize,w_1400/format,webp)
原先的**p2是20,因此加1之后,成为21。
综上所述,读者应该可以了解*和++的使用,是对哪一个地址加1,或对哪一个数值加1,由于++的作用会将原先的地址或变量值加以更新,这里使用粗线条表示其路线。
最后,以3个 的指针作为结尾,请参阅范例starstarstarPointer-5。其余的4个、5个 的指针就留给读者自行研究了。
范例starstarstarPointer-5
/* starstarstarPointer-5.c */
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *s[] = {"Stanford", "University", "California", "America"};
char **sa[] = {s, s+1, s+2, s+3};
char ***p3 = sa;
printf("**p3++ = %s\n", **p3++);
printf("**++p3 = %s\n", **++p3);
printf("**++*p3 = %c\n", **++*p3);
printf("*(*--*++p3+3) = %c\n", *(*--*++p3+3));
system("PAUSE");
return 0;
}
输出结果
![a4b0b4d6a33dfcf80ec02e219b9a0f51e1961bdc](https://yqfile.alicdn.com/a4b0b4d6a33dfcf80ec02e219b9a0f51e1961bdc.png?x-oss-process=image/resize,w_1400/format,webp)
程序中有一条语句如下
char *s[] = {“Stanford”, “University”, “”, “”};```
得知s是一个数组指针,每一个元素都指向字符的指针,其示意图如下。
<div style="text-align: center">
<img src="https://yqfile.alicdn.com/ab179014f98285584d3e680bba88bd6e04fb63e5.png" >
</div>
接下来的语句
char **sa[] = {s, s+1, s+2, s+3};`
sa的每一个元素为指向指针的指针,其示意图如下。
![ee505f9b4d4b8ffb3a25885aeed3ac139369e606](https://yqfile.alicdn.com/ee505f9b4d4b8ffb3a25885aeed3ac139369e606.png?x-oss-process=image/resize,w_1400/format,webp)
最后,p3为3个指针,其初始值为sa,注意p3为指针变量,其示意图如下。
![d3110a249aee849089a5c19a106ba0372a5b88db](https://yqfile.alicdn.com/d3110a249aee849089a5c19a106ba0372a5b88db.png?x-oss-process=image/resize,w_1400/format,webp)
现在看看printf中的语句
**p3++;
由于此处++为后继加,当处理完整个语句后,才会对p3加1(也就是向前挪一个元素),因此,当它输出Stanford字符串后,p3移到sa+1的地方。如下图所示。
![2ac7152f1414664f7dcb98079bc3e45cf5097732](https://yqfile.alicdn.com/2ac7152f1414664f7dcb98079bc3e45cf5097732.png?x-oss-process=image/resize,w_1400/format,webp)
接下来的**++p3与上一语句不同处在于,此处的++是前置加,因此先对p3向前一元素,再输出字符串,结果为California。如下图所示。
![b6f9fca82ae9a29cfb98efddb429ed96f6e62188](https://yqfile.alicdn.com/b6f9fca82ae9a29cfb98efddb429ed96f6e62188.png?x-oss-process=image/resize,w_1400/format,webp)
而++p3的++作用于p3,而p3为sa[2],其值指向s+2,因此sa[2]的值将更改为指向s+3,所以++p3以%c输出的结果为A字符。如下图所示。
![d1b1cf7da9481dfcbb513e57058436e140517fb7](https://yqfile.alicdn.com/d1b1cf7da9481dfcbb513e57058436e140517fb7.png?x-oss-process=image/resize,w_1400/format,webp)
最后一个语句虽然很长,但并不难,让我们来剖析一下。
![90a16f358c37c66dbb6aba8dafd31f32a6434544](https://yqfile.alicdn.com/90a16f358c37c66dbb6aba8dafd31f32a6434544.png?x-oss-process=image/resize,w_1400/format,webp)
因此,语句中的--是针对sa[3]的内容减1,
![268a86bd50b197d48676e450dd2d473e69dc2fce](https://yqfile.alicdn.com/268a86bd50b197d48676e450dd2d473e69dc2fce.png?x-oss-process=image/resize,w_1400/format,webp)
所以(--*++p3+3)的值为i。
当递增运算符(++)、递减运算符(--)与指针运算符()同时出现在一条语句时,必须要掌握++与--的作用点在哪里。由于这3个运算符的运算优先级是相同的,此时必须运用其结合性来由右至左执行。以--++ptr为例,由于结合性由右至左,所以先执行++ptr,再处理(++ptr),接着对++ptr执行--,到此为止,++和--都是针对地址在做运算,最后再执行(--++ptr),这才是值,因为有2个,这才符合访问值的条件。