3

我刚刚阅读了一些关于指针的问题。这是代码:

int a[5]={1, 2, 3, 4, 5};
int *p = (int*)(&a + 1);//second line
cout<<(*p)<<endl;

我的编译器输出是0. 是什么*p?它是指向数组的指针a吗?什么是&a+1手段?

4

4 回答 4

10

这就是您的声明声明的含义:

                           p
                           |
                           v
a[0] a[1] a[2] a[3] a[4] | a[5]
---------(Length)------->

但是你试图得到这个(我假设):

     p
     |
     v
a[0] a[1] a[2] a[3] a[4] |
---------(Length)------->

您需要从声明语句中删除第二组括号以获得a[1]

int * p = (int *)(&a + 1);
// change to this:
int * p = (int *) &a + 1;

您得到错误值的原因与sizeof运算符、运算符优先级和指针算术有关。在我解释错误之前,让我解释一下。


Sizeof 运算符

运算符评估数据类型的sizeof大小(以字节为单位)。直接示例:

sizeof (char)  // always returns 1
sizeof (int)   // usually returns 4 or 8
sizeof (int *) // usually returns 4 or 8

请注意这个有趣的例子:

int a[5];
sizeof (a) // returns sizeof (int) * 5

运算符优先级

运算符优先级是计算一系列运算符的顺序。括号中的任何内容都将首先被评估。计算括号后,由运算符优先级决定表达式的求解顺序。

这是相关的运算符,它们在优先表中的顺序:

Operator    Description    Associativity

()          Parenthesis    Left-to-right
(cast)      Cast           Right-to-left
&           Address of     Right-to-left
+, -        Plus, Minus    Right-to-left

指针算术

指针算术主要是关于将指针与整数(或其他指针)相加、相减和相乘。您需要知道的(对于这个问题的范围)是:

int * p;

p = p + 1;

即使它 + 1,它实际上是在添加sizeof (int)到指针。这是 C 标准编写者的设计选择,因为程序员想要添加sizeof (int)到指针(将它们带到数组中的下一个整数)比添加1(将指针放在第一个和数组中的第二个元素)。

更一般地说:

datatype p;
p = p + 1;
// actually means
p = p + sizeof (datatype);

这是一个相关的例子:

int a[5];
a = a + 1;
// actually means
a = a + sizeof (a);
// remember that sizeof (a) is going to be sizeof (int) * 5?

声明书

回到你的声明:

int * p = (int *)(&a + 1);

您可能已经看到它有什么问题:a + 1真的意味着a + sizeof (a),这使您超出了数组的范围。这可能是0(通常是),也可能是其他随机值。

你可能没有注意到的是:

int * p = (int *) &a + 1;

实际上为您提供了数组中的第二个元素。这与运算符优先级有关。如果您查看我放在链接中的运算符优先级表,则强制转换的优先级高于&+运算符。因此,如果在评估表达式的其余部分之前将其转换a为 a(int *)而不是a[5],则它等效于:

int * a;
a = a + 1; /* and this would
    give you the second element
    in the array */

简单地说,如果您想访问数组中的第二个元素,请更改:

int * p = (int *)(&a + 1);
// to this:
int * p = (int *) &a + 1;
于 2013-10-17T09:42:52.623 回答
8

&a是数组的地址。

&a + 1也是一个地址,但是这个1是什么?它是一个指向sizeof a字节的指针a。所以这就像写作int *p = &a[5];,然后你将其转换为int *.

现在为什么是0?因为a[5]碰巧是0- 请注意,它超出了界限,可能是其他任何东西(未定义的行为)。

请注意,数组是从零开始的,这意味着索引来自0。所以实际上a[4]最后一个元素,a[5]是越界的。

于 2013-10-17T08:50:12.990 回答
5

运算符&用于获取变量的地址。因此,&a是指向 5 个整数数组的指针类型:int (*)[5].

指针算术意味着当你有一个指针时p,thenp+1将指向下一个元素,即sizeof(*p)字节之外的元素。这意味着&a+1指向5*sizeof(int)块之外的块,即数组中最后一个元素之后的块。

转换&a+1int *意味着现在您希望将此新指针解释为指向的指针,int而不是指向 5 个整数数组的指针。你是说,从现在开始,这个指针引用的是sizeof(int)字节长的东西,所以如果你增加它,它会向前移动sizeof(int)单位。

因此,*p正在访问a[5],这是一个超出范围的位置(一个超出最后一个),因此程序具有未定义的行为。它打印了 0,但它可能已经崩溃或打印了其他东西。当发生未定义的行为时,任何事情都可能发生。

于 2013-10-17T09:13:48.823 回答
1
int *p = (int*)(&a+1);

什么是*p?:

在您的代码中,*p是指向类型未知元素int的指针。

让我们推理一下:

&a是一个有效的指针表达式。可以将整数添加到指针表达式。

结果&a:指向整个数组a[5]。它的类型是int (*)[5]

的结果sizeof *(&a)5,它是数组的大小。

的结果&a +1是指针表达式,它保存当前指向的第一个地址int之外的第一个地址。&a

因此,1过去的对象&a*p指向的东西。因此它是类型的未知元素int。因此,访问unknown element是未定义的行为。这也回答了为什么你的输出为零。


边注:

如果你真的想访问数组中的第二个元素,你应该添加1指向数组第一个元素的指针。a指向数组中的第一个元素,大小为sizeof(int*).

结果a+1是数组中第二个元素的地址。

结果*(a+1)是数组中第二个元素的值。2

于 2013-10-17T10:07:12.763 回答