为啥这个指针显示最后一个元素?
Posted
技术标签:
【中文标题】为啥这个指针显示最后一个元素?【英文标题】:Why does this pointer show the last element?为什么这个指针显示最后一个元素? 【发布时间】:2020-10-27 20:30:01 【问题描述】:请向我解释b
指针如何显示最后一个元素。
无论数组有多长,它都会打印出最后一个元素。如果您在cout
中单独使用*b
,则会显示数组外的数字。
#include <iostream>
#include <stdio.h>
using namespace std;
int main()
int a[] = 1,2,3,4,5,6,7,8,9,10,11;
int *b =(int*) (&a+1);
cout << *(b-1) << endl;
return 0;
【问题讨论】:
提示:sizeof(a)
是什么?
顺便说一句,这也是未定义的行为,但我还没有看到编译器不能按预期工作。
【参考方案1】:
这个表达式
&a+1
类型为int ( * )[11]
,指向数组a
最后一个元素之后的内存。
在此声明中
int *b =(int*) (&a+1);
您将表达式解释为具有指向数组 a 的最后一个元素之后的指针类型 int *
。相反,你可以写
int *b = a + 11;
所以表达
b - 1
指向数组a
的最后一个元素。
因此你可以这样想象*( b - 1 )
这个表达式
*( a + 11 - 1 ) => *( a + 10 ) => a[10]
【讨论】:
【参考方案2】:根据pointer arithmetic 规则,将指针递增/递减N
元素会将指针的值调整N * sizeof(T)
字节,其中T
是指针的取消引用类型。
&a
是指向数组本身的指针,它的类型为int[11]
,因此您有一个指向数组开头的int(*)[11]
类型的指针。让我们在下图中将此称为A1
。
向该指针添加 +1 会将其前移sizeof(int[11])
(又名sizeof(int)*11
)字节,从而生成一个新的int(*)[11]
指向紧跟整个数组的内存地址的指针。我们在图中将其称为A2
。
然后类型转换这个新指针,所以现在你有一个指向数组末尾的int*
类型的指针。这是您分配给int *b
指针变量的内存地址。让我们在下图中将此称为B1
。
从该指针中减去 -1 会将其减少 sizeof(int)
字节,从而产生一个新的 int*
指针,指向数组中最后一个 int
元素的内存地址。让我们在下图中将此称为B2
。
因此,当您取消引用 b
以打印它所指向的 int
时,您正在打印数组中最后一个 int
的值。如果您不递减b
,则它指向数组的末尾,并且您有未定义的行为。您可能只是打印出随机垃圾,或者您的应用程序可能会崩溃。任何事情都有可能发生。
---------------------------------------------------------------------
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
---------------------------------------------------------------------
^ ^ ^
| | |
|_A1 --------------------------------------------------------|----->|_A2
+1 | |
|_B2 <-|_B1
-1
【讨论】:
其实就是UB。在数组类型之间转换指针到数组元素的类型是UB。但是,这种事情经常发生,std::complex
甚至开辟了一个例外,以便可以使用已知结构调用 C 函数。
@doug "在一个数组类型之间转换一个指针,到数组元素的类型是UB。" - UB怎么样?根据定义,就指针算术而言,指针可以指向对象的开头或结尾。所以我们获得了一个指向数组末尾的有效指针,将该指针转换为元素类型不会更改地址,只会更改指针类型。现在,强制转换的指针可以指向数组任何元素的开头或结尾,只要它仅在数组范围内被取消引用,我认为行为应该是明确定义的
T 的数组与 T 的类型不同。没有 UB,您不能将指针从一种类型转换为另一种类型(有一些像有符号无符号这样的剥离)。也就是说,这样做并不少见,据我所知,所有编译器都会产生预期的代码。就个人而言,我认为这不应该是 UB,但我不编写标准。以上是关于为啥这个指针显示最后一个元素?的主要内容,如果未能解决你的问题,请参考以下文章