指针和数组混淆
我们有
int a[5]={10, 20, 30, 40, 50};
我想知道以下两个代码段是如何做的?
int *ptr = (int *)(&a+1); int *t = (int *)(&a -1);
如果我们有
printf("%d %d %d \n", *(a+1), *(ptr-1), *(t+1));
应该是什么结果?
所有问题都来自于使用&a
,它是一个指向“五个整数数组”的指针,因此指针算法(当你考虑地址时)被sizeof(a)
“缩放”(可能是例如20如果int
是4个字节,并且编译器不需要填充用于对齐目的 – 合理的假设,当然远非确定。
所以,之后
int *ptr = (int *)(&a+1); int *t = (int *)(&a -1);
ptr
是指向内存地址“sizeof(a)大于地址a”的int的指针, t
类似于“sizeof(a)小于a的地址”。 因此…:
printf("%d %d %d \n", *(a+1), *(ptr-1), *(t+1));
应该是什么结果?
很可能是分段违规,否则20
后跟两个完全任意的整数值。 由于ptr
和t
是int
指针,因此它们的-1
和+1
的地址算术缩放不会补偿在&a
上完成&a
(内存地址的缩放是由sizeof(int)
而不是sizeof(a)
!),因此ptr-1
和t+1
指向(涉嫌;-) int
s,它们分别是“在一个结束后的几个int
”和“在a开始之前的几个int
”。
没有办法知道在那些任意地址是否有任何进程被允许解决的内存(分词中断的可能性),以及,如果存在任何可访问的内存,其内容“被视为int
”可能是什么是。
编辑 :@caf指出ptr - 1
无效 – 它正确指向a
的最后一个元素; 所以输出(除非有一个分段错误,@ NullUserException认为这是不太可能的,但在这一点上我们不同意;-)将在第三个“任意”垃圾之前以20 50
开始。 根据C标准,点是有效的计算(尽管不使用)指针“只是一个结束”的数组, 并且数组的大小必须完全是该数组的长度时间其元素的大小(填充)如果需要,允许元素的类型,如果需要,它将显示在元素自己的sizeof中,但不是整个数组中的元素。 微妙但重要的;-)。
由于a的类型是5-array的数组 ,这意味着&a
的类型是指向5-array的数组 。
当您从指针添加或减去1时,您要求它指向内存中该类型的下一个或上一个对象。 所以&a+1
正在内存(不存在)之后立即创建一个指向5-数组的指针,而&a-1
正在创建一个指向5-数组的指针。在记忆中(也不存在)。 在内存中,它看起来像这样(每个单元格代表一个int
):
Address: &a-1 &a &a+1 Contents: | ? | ? | ? | ? | ? | 10 | 20 | 30 | 40 | 50 | ? | ? | ? | ? | ? |
当在表达式*(a+1)
,它将转换为指向其第一个元素的指针 – 因此指向10
值的指针指向int
。 添加一个然后使指针指向下一个int
– a+1
点在20
值。 *(a+1)
然后获取该值,因此打印的第一个数字是20。
由于ptr
也是指向int
的指针,这意味着ptr - 1
在ptr
之前创建一个指向int
的指针 – 在这种情况下,它将指向50.因此打印的第二个数字是50。
类似地, t + 1
在t
之后立即创建一个指向int
的指针 – 在这种情况下,它是第二个?
在上图中。 这是一个未初始化的值 – 它可以打印任何内容,甚至可以使程序崩溃。
Address: &a-1 &a &a+1 t t+1 a a+1 ptr-1 ptr Contents: | ? | ? | ? | ? | ? | 10 | 20 | 30 | 40 | 50 | ? | ? | ? | ? | ? |
“应该是什么结果”?
下次你想知道像这样的小代码片段应该做什么,请查看: http : //ideone.com/4fCud
我得出的结果是:
20 50 134520820
编辑:
当你运行一个程序时,看到这样的输出,然后发现自己在问“ 这个值来自哪里?” 您可能遇到了未定义的行为。
在这种情况下,第三个值并不指向您可能认为指向的位置。 它正在读取未初始化的内存(最有可能),由进程空间中的代码拥有的内存,但是在程序之外(您加载的库或C运行时),或者与该程序无关的内存(更少)可能,因为受保护的内存)。
让我们一块一块地看一下。
&a
表示&a
的地址。 因此,它获取整数10的地址。
&a+1
是下一个指针。 所以它是存储在变量a
之后的东西。 馊主意。
&a-1
是存储在内存之前的东西。 再次,糟糕的主意。
*(a+1)
是a指向的位置加上一个整数。 那将是a[1]
或20。
*(ptr-1)
是a
,因为ptr
是&a+1
,所以ptr-1
是&a
。 它是a的指针值。 将其打印为%d
是一个错误。 如果你说**(ptr-1)
,你会从printf
得到一个更有意义的10
。
*(t+1)
也是a
,如上所述,但切换了优缺点。