我刚读了一些关于指针的问题。这是代码:
int a[5]={1, 2, 3, 4, 5};
int *p = (int*)(&a + 1);//second line
cout<<(*p)<<endl;
我的编译器输出是0
。什么是*p
?它是指向数组a
的指针吗?什么是&a+1
的意思?
答案 0 :(得分:8)
&a
是数组的地址。
&a + 1
也是一个地址,但这是什么1?它是指向sizeof a
的 a
字节的指针。所以就像写int *p = &a[5];
,然后你把它投到int *
。
现在为什么0?因为a[5]
碰巧是0
- 请注意它不在界限范围内,可能是其他任何东西(未定义的行为)。
请注意,数组从零开始,这意味着索引来自0
。实际上a[4]
实际上是最后元素,a[5]
超出范围。
答案 1 :(得分:8)
这是您的声明声明的含义:
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
运算符,operator precedence和指针算法有关。在解释错误之前,让我解释一下。
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
被强制转换为(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;
答案 2 :(得分:5)
运算符&
用于获取变量的地址。因此,&a
是指向5个int的数组的类型指针:int (*)[5]
。
指针算术意味着当你有一个指针p
时,p+1
将指向下一个元素,即sizeof(*p)
个字节。这意味着&a+1
指向5*sizeof(int)
块,即数组中最后一个元素之后的块。
将&a+1
强制转换为int *
意味着现在您希望将此新指针解释为指向int
的指针,而不是指向5个整数数组的指针。你说,从现在开始,这个指针会引用sizeof(int)
个字节长的内容,所以如果你增加它,它将向前移动sizeof(int)
个单位。
因此,*p
正在访问a[5]
,这是一个超出界限的位置(超出最后一个位置),因此程序具有未定义的行为。它打印0,但它可能已经崩溃或打印其他东西。当未定义的行为发生时,任何事情都可能发生。
答案 3 :(得分: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