c -这是写数组指针的过程



这是写数组指针的过程。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]数组的引用,然后衰变成指向第一个intint*指针。

:

pj = *pi

pj = &(*pi)[0]
即当前int[4]数组中第一个int的地址相同。

*pi + 4

&(*pi)[0] + 4aka&(*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指定整行,而不仅仅是其中的一个元素。此处指针的类型很重要。