首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >指针问题

指针问题
EN

Stack Overflow用户
提问于 2013-08-25 06:47:46
回答 4查看 233关注 0票数 5

我创建了指向指针和int数组的指针,但是当我试图通过指针访问数组时,它跳过一些元素并一次由两个元素移动(例如:从1到3)。这是我的代码:

代码语言:javascript
复制
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的值相同?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2013-08-25 08:57:27

好的,这个问题已经回答了,答案也被接受了,但是即使是被接受的答案也不能解释最初的海报看到的奇怪的结果:为什么y[1]y[2]打印5和8?这是解释。

发信人:你从以下陈述中得到了什么输出?

代码语言:javascript
复制
printf ("Size of integer: %zu\n", sizeof (int));
printf ("Size of pointer: %zu\n", sizeof (int*));

我打赌输出是:

代码语言:javascript
复制
Size of integer: 4
Size of pointer: 8

换句话说,我猜您是在64位计算机上编译的,其中整数的大小是4字节,指针的大小是8字节。基于这个假设,下面是正在发生的事情。

p是一个数组。除了少数例外,当在任何表达式中使用时,数组的名称“衰变”到指向其第一个元素的指针。因此,每当您访问p的值时,它都会产生第一个元素的地址。

&p是数组“衰变”到指针规则的例外之一。当应用于数组的名称时,地址运算符返回指向整个数组的指针--而不是指向数组第一个元素的指针。

这意味着p&p具有相同的值,但它们在语义上非常不同。打印时,您将得到相同的值:

代码语言:javascript
复制
 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时:

代码语言:javascript
复制
int* x = p;
int** y = &p;

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

此外,声明y的方式与您要分配给它的值之间存在不匹配。&p的类型是int (*) [5];指向5 int数组的指针。y只是指向单个int的指针。您的编译器应该给您一个关于这种不匹配的警告。我的有:

代码语言:javascript
复制
Warning: incompatible pointer types assigning to 'int**' from 'int (*) 5'

这种不匹配解释了在打印y[1]y[2]值时出现的奇怪结果。让我们来看看这些值是怎么回事。

如您所知,数组下标是从数组开始时的抵消:

代码语言:javascript
复制
x[0] == *(x + 0)

因此,x[0]产生数组的第一个元素,即2。

代码语言:javascript
复制
x[1] == *(x + 1)

但是x是一个指向int的指针。那么,在加法x + 1中到底发生了什么呢?记住指针算术是如何工作的。向指针中添加整数意味着实际上要将整数乘以指向的.元素的大小(在本例中为:

代码语言:javascript
复制
x + 1 == x + (1 * sizeof(int))

由于系统上的sizeof(int)是4,所以x[1]的值是数组中的下一个整数,即3。

那么,当您打印y[0]时,这是如何评估的呢?

代码语言:javascript
复制
y[0] == *(y + 0)

因此,打印位于y指向的地址的值,即p地址。这是p的第一个元素,因此得到了结果2。

打印y[1]时会发生什么?

代码语言:javascript
复制
y[1] == *(y + 1)

但是y是什么呢?这是一个pointer to a pointer to an int。因此,当您将1添加到y时,指针算法的工作方式是再次添加1*指向的元素类型的大小。

代码语言:javascript
复制
y + 1 == y + (1 * sizeof (int*))

int*的大小是8个字节,而不是4个字节!因此,每次增量y 1时,都会增加8个字节,即两个整数的大小。因此,当您取消对该值的引用时,您将得到的不是数组中的下一个整数,而是距数组2的整数。

为了更清楚地解释:让我们假设数组从元素1000开始。然后,由于每个int需要4个字节,因此如下所示:

代码语言:javascript
复制
 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字节。不应将int (*) []分配给int**。您不应该取消指向指向int的指针的引用,并期望得到int结果。总是注意编译器警告。

票数 5
EN

Stack Overflow用户

发布于 2013-08-25 07:07:28

这至少提供了一个清晰的编译,并使用%p打印指针:

代码语言:javascript
复制
#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 10.8.4,GCC 4.8.1,64位编译):

代码语言:javascript
复制
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
票数 4
EN

Stack Overflow用户

发布于 2013-08-25 07:27:41

记住,数组名称在大多数表达式中很容易变成指向第一个元素的指针。内存中的p[]数组类似于(地址是假设的):

代码语言:javascript
复制
  p 
 200   204 208  212  216 
+----+----+----+----+---+
|  2 |  3 | 5  | 6  | 8 |
+----+----+----+----+---+
  ▲    ▲    ▲    ▲    ▲
  |    |    |    |    | 
  p    p+1  p+2  p+3  p+3

x = p;之后,x也指向第一个元素。

代码语言:javascript
复制
  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编译的警告(或错误))。

读:p

因为p&p在价值上是相同的,所以y的地址值也与p相同。

代码语言:javascript
复制
  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**

你的第一张照片:

代码语言:javascript
复制
 printf("p value is %d and p points to %d",p,&p);

应写为:

代码语言:javascript
复制
 printf("p value is %p and p points to %p", (void*)p, (void*)&p);
 //                  ^                  ^  

使用%p而不是%d,因为您正在打印地址,而且void*的类型也是必要的,因为%p需要void*。正如我所说,按价值计算,p&p是相同的,两个地址都是相同的。

第二印件f:

代码语言:javascript
复制
printf("\n x is %d \n", x[1]);

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

第三印件f:

代码语言:javascript
复制
printf("\n y is %d \n", y[0]);

y类型为int**,因此y[0] == *(y + 0) = *y值存储在y类型*y is int*中。

因此,由于y[0]int*,所以您应该使用%p而不是%d

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

类似地,y[1]打印3y[2]打印5

编辑

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

不,正如我前面解释过的那样,它输出@码页,其中sizeof(int) == sizeof(int*)可能在您的系统中sizeof(int*) = sizeof(int)的两倍(64位编译器),所以当您在下一个位置旁边添加一个地址时。

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

更正你的代码。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18426474

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档