我遇到了这段代码,发现很奇怪,它可以打印5。我不明白为什么将int *强制转换为基本上是一维数组的原因。有人可以解释吗?
int main()
{
int v[2][3] = {{1,2,3},{4,5,6}};
int* c = (int*)v;
printf("%d\n", c[4]);
}
让我们谈谈数组下标的工作原理。
[首先,请记住,数组下标操作a[i]
被定义为*(a + i)
-也就是说,给定地址a
,从该地址偏移i
个元素(不是字节!)并取消引用结果:
+---+
a: | |
+---+
| |
+---+
...
+---+
| | <--- a + i
+---+
这有两个原因:
除非它是sizeof
,_Alignof
或一元&
运算符的操作数,或者是用于在声明中初始化字符数组的字符串文字,否则expression类型为“ N” -T
“的-element数组将被转换(“ decay”)为“ pointer to T
”类型的表达式,该表达式的值将成为数组第一个元素的地址。如果T
本身是数组类型(例如int [2][3]
或“ int
的3元素数组的2元素数组”),则最后将指针指向数组类型(int (*)[3]
,或“指向int
的3元素数组的指针。
指针算术考虑了指向类型的大小-如果p
是int *
并存储int
对象的地址,则p + 1
产生下一个int
] object,而不是下一个字节。如果p
是指向int
(int (*)[3]
)的3元素数组的指针并存储数组的地址,则p + 1
产生C0]的下一个3元素数组的地址。 。
图解:
int
[左图是具有 int int * int [3] int (*)[3]
+---+ +---+
| 1 | <--- p | 1 | <--- p
+---+ + - +
| 2 | <--- p + 1 | 2 |
+---+ + - +
| 3 | | 3 |
+---+ +---+
| 4 | | 4 | <--- p + 1
+---+ + - +
| 5 | | 5 |
+---+ + - +
| 6 | | 6 |
+---+ +---+
指针的int
对象的序列,而右图是具有int *
指针的int [3]
对象的序列。
所以发生的是,您正在获取int (*)[3]
的第一个元素的地址,但是您将其视为v
,而不是int *
:
int (*)[3]
您基本上是在右边拍摄照片,并假装是左边的照片。因此,int* c = (int*)v;
对应于序列中的第5个元素,在这种情况下为c[4]
。
现在,此行为是未定义-5
和int (*)[3]
不是兼容类型,并且不能保证以此方式尝试访问int *
的元素。使用哪种更安全]
v
没有强制转换可以解决不兼容的类型,并且可以获得完全相同的效果。
该标准未明确声明可以将一组相同类型的连续对象用作数组。
尽管这样做可能会并且可以正常工作很多次,但您不能依靠它始终工作。
您可以使用指针指向数组类型int *c = &v[0][0]; // int * = int *
:
(*arr)[]
请注意,索引符号类似于2D数组的索引符号。
或简单地使指针指向您要访问的特定索引的地址。
int v[2][3] = {{1,2,3},{4,5,6}};
int (*c)[3] = v;
printf("%d\n", c[0][4]);