5

我创建了一个指向指针和一个int数组的指针,但是当我尝试通过指向指针的指针访问数组时,它会跳过一些元素并一次移动两个元素(例如:从 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 值

4

4 回答 4

5

好的,这个问题已经得到回答,并且已经接受了一个答案,但即使是接受的答案也不能解释原始发帖人看到的奇怪结果:为什么要打印 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 个整数数组的地址。

所以当你定义xy如下:

int* x = p;
int** y = &p;

x分配一个指向数组第一个元素的指针;y被分配一个指向整个数组的指针。这是一个重要的区别!

y此外,声明方式与您分配给它的值之间存在不匹配。&p是类型int (*) [5];指向 5 数组的指针inty仅仅是一个指向单个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 个!因此,每次增加y1 时,都会增加 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结果。并始终注意编译器警告

于 2013-08-25T08:57:27.393 回答
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
于 2013-08-25T07:07:28.697 回答
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]并且yint**。(你必须得到一个警告(或错误)编译-Wall)。

阅读:和之间的区别 &pp

因为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:

 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:

printf("\n x is %d \n", x[1]);

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

第三个printf:

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

注意ytype is int**so y[0]== *(y + 0)= *yvalue 存储在ytype of *yis int*
再说一遍,因为你应该使用而y[0]不是.int*%p%d

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

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

编辑

当我打印时,y[1]它将打印5而不是打印为. 3y[2]8

不,它输出如我上面解释的检查@codepade where sizeof(int)==sizeof(int*)可能在您的系统中= (64 位编译器)的sizeof(int*)两倍,因此当您添加一个时,您将在下一个位置旁边寻址。sizeof(int)

正如@WhozCraig 评论的那样:试试y = &x;你可能会有更好的运气。并打印你传递的任何应该是“指针”的东西%p

更正您的代码。

于 2013-08-25T07:27:41.983 回答
0

我认为您的代码无法成功编译。的类型&pchar (*p)[10],但类型ychar **

cannot convert from 'char (*)[10]' to 'char **'
于 2013-08-25T09:06:10.320 回答