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