这就是写数组指针的过程。
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");
}
}
当你引用一个固定数组本身时,它可以decay变成指向它的第一个元素的指针。
arr
是一个int[3][4]
数组,即3个元素的数组,其中每个元素是一个int[4]
数组。
pi
是指向 int[4]
数组的指针。
因此:
pi = arr
pi = &arr[0]
arr + 3
&arr[0] + 3
又名&arr[3]
因此,外层循环遍历元素
arr[0]
..arr[2]
,其中pi
在每次迭代中指向当前元素。
pj
是指向 int
的指针。 *pi
产生对当前 int[4]
数组的引用,然后它 decays 成一个 int*
指针指向它的第一个 int
.
因此:
pj = *pi
pj = &(*pi)[0]
int
数组中第一个int[4]
的地址。
*pi + 4
&(*pi)[0] + 4
又名&(*pi)[4]
int
数组中第4个int[4]
之后的地址。
因此,内部循环遍历元素
(*pi)[0]
..(*pi)[3]
,其中pj
在每次迭代中指向当前元素。
也许下面的图表会对你有所帮助。
想想
arr
在内存中是这样布局的(不是真的,但是可以treated好像是):
+----+----+-----+---+
| 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 |
+----+----+----+----+
^
当内循环完成时,外循环的第 2 次迭代运行,其中
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 |
+----+----+----+----+
^
当内循环结束时,外循环的第 3 次也是最后一次迭代运行,其中
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
是5.*pi
最后的结论是错误的。
pi
确实包含一行的地址,所以 *pi
指定 整行,而不仅仅是它的一个元素。指针的类型在这里很重要。