我创建了一个指向指针和int
数组的指针,但是当我尝试通过指向指针的指针访问数组时,它会跳过一些元素并一次移动两个元素(例如:从1到1) 3)。
这是我的代码:
int main(void) {
int c=10;
int p[5]={2,3,5,6,8};
int *x;
int **y;
x=p;
y=&p;
printf("p value is %d and p points to %d",p,&p);
printf("\n x is %d \n",x[1]);
printf("\n y is %d \n",y[0]);
return 0;
}
当我打印y[1]
时,它将打印5而不是3,y[2]
打印为8.我想不出原因。谁可以帮我这个事?指针x
工作正常并沿着x[0]=2
,x[1]=3
,x[5]=5
的正确元素移动。
也可以解释为什么我得到p和& p
答案 0 :(得分:5)
好的,这个问题已得到回答并且答案已被接受,但即使是接受的答案也没有解释原始海报看到的奇怪结果:为什么y[1]
和y[2]
打印5和8?这是解释。
原创海报:您从以下声明中得到什么结果?
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。
那么,当您打印y[0]
时,如何评估?
y[0] == *(y + 0)
因此,打印y
指向的地址处的值,即p
的地址。这是p
的第一个元素,因此您得到结果2.
打印y[1]
时会发生什么?
y[1] == *(y + 1)
但是y
是什么?这是pointer to a pointer to an int
。因此,当您向y
添加1时,指针算法的工作方式是再次添加1 *指向的元素类型的大小。
y + 1 == y + (1 * sizeof (int*))
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
当您向x添加1时,您正在添加1 * sizeof(int)
,即您实际上正在添加4.因此您获得1004,而*(x + 1)
或x[1]
会为您提供3。
但是当你向y添加1时,你正在添加1 * sizeof(int*)
,即你实际上是在添加8.所以你得到1008,*(y + 1)
给你地址1008的元素,或者5.
这解释了您获得的输出。但是,这不是一种合理的编码方式。你不应该指望指针的大小总是8个字节。您不应将int (*) []
分配给int**
。您不应取消引用指向int
的指针,并期望获得int
结果。并且始终注意编译器警告。
答案 1 :(得分:4)
这至少提供了一个干净的编译并使用%p
来打印指针:
#include <stdio.h>
int main(void)
{
int p[5]={2,3,5,6,8};
int *x = p;
int **y = &x;
printf("p value is %p and the address of p is %p and p points to %d\n", (void *)p, (void *)&p, *p);
printf("x[1] is %d\n", x[1]);
printf("y[0] is the address %p\n", (void *)y[0]);
printf("y[0][0] is %d\n", y[0][0]);
return 0;
}
示例输出(Mac OS X 10.8.4,GCC 4.8.1,64位编译):
p value is 0x7fff5a1a54d0 and the address of p is 0x7fff5a1a54d0 and p points to 2
x[1] is 3
y[0] is the address 0x7fff5a1a54d0
y[0][0] is 2
答案 2 :(得分:4)
请记住,在大多数表达式中,数组名称很容易衰减为指向第一个元素的指针。
内存中的p[]
数组就像(地址是假设的):
p
200 204 208 212 216
+----+----+----+----+---+
| 2 | 3 | 5 | 6 | 8 |
+----+----+----+----+---+
▲ ▲ ▲ ▲ ▲
| | | | |
p p+1 p+2 p+3 p+3
在x = p;
之后,x
也指向第一个元素。
p
200 204 208 212 216
+----+----+----+----+---+
| 2 | 3 | 5 | 6 | 8 |
+----+----+----+----+---+
▲ ▲ ▲ ▲ ▲ ▲
| | | | | |
| p p+1 p+2 p+3 p+3
|
x
+----+
| 200|
+----+
在表达式y = &p;
中,&p
是int(*)[5]
类型数组的指针,y
是int**
。 (您必须使用-Wall
编译警告(或错误)。
阅读:Difference between &p
and p
由于p
和&p
的价值相同,因此y
的地址值也与p
相同。
p
200 204 208 212 216
+----+----+----+----+---+
| 2 | 3 | 5 | 6 | 8 |
+----+----+----+----+---+
▲ ▲ ▲ ▲ ▲ ▲
| | | | | |
| p p+1 p+2 p+3 p+3
|
x y
+----+ +----+
| 200| | 200|
+----+ +----+
int* int**
你的第一张照片:
printf("p value is %d and p points to %d",p,&p);
应写成:
printf("p value is %p and p points to %p", (void*)p, (void*)&p);
// ^ ^
使用%p
代替%d
,因为您打印的地址也必须void*
,因为%p
需要void*
。正如我所说,价值方面p
和&p
相同,两个地址都相同。
第二次印刷:
printf("\n x is %d \n", x[1]);
输出:3
,x
指向数组中的第一个元素x[1]
== *(x + 1)
== 3
(在我的数字x + 1
中== 204)。
第三次印刷:
printf("\n y is %d \n", y[0]);
注意y
类型为int**
,因此y[0]
== *(y + 0)
= *y
值存储在y
类型*y
是int*
再次因为y[0]
为int*
,您应该使用%p
而不是%d
。
但是上面的printf语句打印的第一个元素的值是:2
。
同样y[1]
打印3
和y[2]
打印5
。
修改强>
当我打印
y[1]
时,它将打印5
而不是3
,y[2]
打印为8
。
不,它按照我上面的解释输出检查@ codepade其中sizeof(int)
== sizeof(int*)
可能在您的系统中sizeof(int*)
= sizeof(int)
的两倍(64 -bit编译器)所以当你添加一个你在下一个位置旁边的地址时。
正如@WhozCraig评论的那样:试试y = &x;
你可能会有更好的运气。打印你传递的任何应该是%p
的“指针”。
更正您的代码。
答案 3 :(得分:0)
我认为您的代码无法成功编译。
&p
的类型为char (*p)[10]
,但y
的类型为char **
,
cannot convert from 'char (*)[10]' to 'char **'