这是写数组指针的过程。int *pj = *pi;
,*pi
是数组的值,为什么没有错误呢?(假设pi
包含第二行的地址值,我认为*pi
为5。如果5是地址值?)
我不明白。
#include <stdio.h>
int main()
{
int arr[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
for(int (*pi)[4]=arr; pi<arr+3; pi++){
for(int *pj = *pi; pj<*pi+4;pj++){
printf("%4d",*pj);
}
printf("n");
}
}
当你引用一个固定数组时,它可以衰变为指向它的第一个元素的指针。
arr
是一个int[3][4]
数组,即一个包含3个元素的数组,其中每个元素都是一个int[4]
数组。
pi
是指向int[4]
数组的指针。
:
pi = arr
与pi = &arr[0]
相同,即外部数组第一个元素的地址。
arr + 3
与&arr[0] + 3
或&arr[3]
相同,即外部数组第3个元素之后的地址。
因此,外部循环遍历元素arr[0]
..arr[2]
,其中pi
指向每次迭代的当前元素。
pj
是指向int
的指针。*pi
产生对当前int[4]
数组的引用,然后将衰变成指向第一个int
的int*
指针。
:
pj = *pi
与pj = &(*pi)[0]
即当前int[4]
数组中第一个int
的地址相同。
*pi + 4
与&(*pi)[0] + 4
aka&(*pi)[4]
相同,即当前int[4]
数组中第四个int
之后的地址。
因此,内部循环遍历元素(*pi)[0]
..(*pi)[3]
,其中pj
指向每次迭代的当前元素。
也许下面的图表会对你有所帮助。
想象arr
像这样在内存中布局(它不是真的,但它可以被当作来对待):
+----+----+-----+---+
| 1 | 2 | 3 | 4 |
+----+----+----+----+
| 5 | 6 | 7 | 8 |
+----+----+----+----+
| 9 | 10 | 11 | 12 |
+----+----+----+----+
在外部循环的第一次迭代中,pi
指向这里:
+----+----+-----+---+
-> | 1 | 2 | 3 | 4 |
+----+----+----+----+
| 5 | 6 | 7 | 8 |
+----+----+----+----+
| 9 | 10 | 11 | 12 |
+----+----+----+----+
在内部循环的每次迭代中,pj
遍历内部数组:
+----+----+-----+---+
| 1 | 2 | 3 | 4 |
+----+----+----+----+
^
+----+----+-----+---+
| 1 | 2 | 3 | 4 |
+----+----+----+----+
^
+----+----+-----+---+
| 1 | 2 | 3 | 4 |
+----+----+----+----+
^
+----+----+-----+---+
| 1 | 2 | 3 | 4 |
+----+----+----+----+
^
当内部循环结束时,外部循环的第二次迭代运行,pi
现在指向这里:
+----+----+-----+---+
| 1 | 2 | 3 | 4 |
+----+----+----+----+
-> | 5 | 6 | 7 | 8 |
+----+----+----+----+
| 9 | 10 | 11 | 12 |
+----+----+----+----+
和pj
现在遍历内部数组:
+----+----+----+----+
| 5 | 6 | 7 | 8 |
+----+----+----+----+
^
+----+----+----+----+
| 5 | 6 | 7 | 8 |
+----+----+----+----+
^
+----+----+----+----+
| 5 | 6 | 7 | 8 |
+----+----+----+----+
^
+----+----+----+----+
| 5 | 6 | 7 | 8 |
+----+----+----+----+
^
当内部循环结束时,外部循环的第三次也是最后一次迭代运行,pi
现在指向这里:
+----+----+-----+---+
| 1 | 2 | 3 | 4 |
+----+----+----+----+
| 5 | 6 | 7 | 8 |
+----+----+----+----+
-> | 9 | 10 | 11 | 12 |
+----+----+----+----+
和pj
现在遍历内部数组:
+----+----+----+----+
| 9 | 10 | 11 | 12 |
+----+----+----+----+
^
+----+----+----+----+
| 9 | 10 | 11 | 12 |
+----+----+----+----+
^
+----+----+----+----+
| 9 | 10 | 11 | 12 |
+----+----+----+----+
^
+----+----+----+----+
| 9 | 10 | 11 | 12 |
+----+----+----+----+
^
当两个循环结束时,整个数组都被遍历了,并且每个int
都被打印出来了。
给定arr
的定义…
int arr[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
…在pi
的定义中…
int (*pi)[4]=arr;
,子表达式arr
(数组类型为int[3][4]
)被自动转换为指向第一个数组元素的指针。该元素的数组类型为int[4]
,指向它的指针类型为int(*)[4]
,这与为pi
声明的类型完全匹配。所以一切都很好。
pj
的定义:
int *pj = *pi;
pi
的类型是int(*)[4]
,所以*pi
的类型是int[4]
,这是一个数组类型。由于它具有数组类型,因此该子表达式的值自动转换为指向其第一个元素的指针,在本例中是int
。指向它的指针的类型是int *
,它与为pj
声明的类型完全匹配,所以,一切都很好。
假设
pi
包含第二行的地址值,我认为*pi
为5。
最后一个结论是错误的。pi
不包含一行的地址,因此*pi
指定整行,而不仅仅是其中的一个元素。此处指针的类型很重要。