指向指针问题的指针

时间:2013-08-25 06:47:46

标签: c pointers

我创建了一个指向指针和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]=2x[1]=3x[5]=5的正确元素移动。 也可以解释为什么我得到p和& p

的相同值

4 个答案:

答案 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个整数数组的地址。

因此,当您按如下方式定义xy时:

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;中,&pint(*)[5]类型数组的指针,yint**。 (您必须使用-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]);

输出:3x指向数组中的第一个元素x[1] == *(x + 1) == 3(在我的数字x + 1中== 204)。

第三次印刷:

printf("\n y is %d \n", y[0]);

注意y类型为int**,因此y[0] == *(y + 0) = *y值存储在y类型*yint* 再次因为y[0]int*,您应该使用%p而不是%d

但是上面的printf语句打印的第一个元素的值是:2

同样y[1]打印3y[2]打印5

修改

  

当我打印y[1]时,它将打印5而不是3y[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 **'