好的,这个问题已经得到回答,并且已经接受了一个答案,但即使是接受的答案也不能解释原始发帖人看到的奇怪结果:为什么要打印 5 和 8 y[1]
?y[2]
这是解释。
原始海报:您从以下陈述中得到什么输出?
printf ("Size of integer: %zu\n", sizeof (int));
printf ("Size of pointer: %zu\n", sizeof (int*));
我敢打赌,输出是:
Size of integer: 4
Size of pointer: 8
换句话说,我猜你正在一个 64 位机器上编译,其中整数的大小是 4 个字节,指针的大小是 8 个字节。基于这个假设,这就是正在发生的事情。
p
是一个数组。除了少数例外,当在任何表达式中使用时,数组的名称“衰减”为指向其第一个元素的指针。因此,每当您访问 的值时p
,它都会产生其第一个元素的地址。
&p
是关于数组“衰减”到指针的规则的例外之一。address-of 运算符在应用于数组名称时,返回指向整个数组的指针,而不是指向数组第一个元素的指针。
这意味着p
和&p
具有相同的值,但它们在语义上非常不同。打印时您将获得相同的值:
printf("p value is %p and p points to %p", p, &p); // use %p and not %d for addresses
但是,这并不意味着p
和&p
指代同一个东西。p
是数组的第一个元素的地址,即&p[0]
. 另一方面,&p
是整个 5 个整数数组的地址。
所以当你定义x
和y
如下:
int* x = p;
int** y = &p;
x
分配一个指向数组第一个元素的指针;y
被分配一个指向整个数组的指针。这是一个重要的区别!
y
此外,声明方式与您分配给它的值之间存在不匹配。&p
是类型int (*) [5]
;指向 5 数组的指针int
。y
仅仅是一个指向单个int
. 你的编译器应该给你一个关于这种不匹配的警告。我的做:
Warning: incompatible pointer types assigning to 'int**' from 'int (*) 5'
这种不匹配解释了打印y[1]
和的值时的奇怪结果y[2]
。让我们看看这些值发生了什么。
如您所知,数组下标是数组开头的偏移量:
x[0] == *(x + 0)
因此x[0]
产生数组的第一个元素,即 2。类似地
x[1] == *(x + 1)
但是x
是一个指向 int 的指针。那么在加法中到底发生了什么x + 1
?记住指针算法是如何工作的。将整数添加到指针意味着您实际上是将该整数乘以所指向元素的大小。在这种情况下:
x + 1 == x + (1 * sizeof(int))
因为sizeof(int)
在您的系统上是 4,所以 的值x[1]
是数组中的下一个整数,即 3。
那么,当您 print 时y[0]
,这是如何评估的?
y[0] == *(y + 0)
因此,在 指向的地址处的值,y
即在 的地址处的值p
被打印出来。这是 的第一个元素p
,因此您得到结果 2。
打印时会发生什么y[1]
?
y[1] == *(y + 1)
但什么是y
?它是一个pointer to a pointer to an int
. 因此,当您将 1 添加到 时y
,指针运算的工作方式是再次添加 1 * 指向的元素类型的大小。
y + 1 == y + (1 * sizeof (int*))
an 的大小int*
是 8 个字节,而不是 4 个!因此,每次增加y
1 时,都会增加 8 个字节,即两个整数的大小。因此,当您取消引用该值时,您得到的不是数组中的下一个整数,而是两个相距的整数。
为了更清楚地解释:让我们假设数组从元素 1000 开始。然后,因为每个int
需要四个字节,所以情况如下:
Address Element
-----------------------
1000 2
1004 3
1008 5
1012 6
1016 8
p == &p == x == y == 1000
*x == *y == 2
当您将 1 添加到 x 时,您添加了 1 * sizeof(int)
,即,您实际上添加了 4。所以您得到 1004,并且*(x + 1)
或x[1]
给您 3。
但是当你将 1 加到 y 时,你加了 1 * sizeof(int*)
,也就是说,你实际上加了 8。所以你得到 1008,并*(y + 1)
给你地址 1008 或 5 的元素。
这解释了你得到的输出。然而,这不是一种合理的编码方式。你不应该期望指针的大小总是 8 个字节。您不应将 an 分配int (*) []
给int**
. 您不应取消对指向 an 的指针的引用int
并期望得到int
结果。并始终注意编译器警告。