c语言中的奇怪输出问题 1)#包括 int main() { int a[5]={1,2,3,4,5}; int*ptr=(int*)(&a+1); printf(“%d%d”,*(a+1),*(ptr-1)); 返回0; }
输出为25c语言中的奇怪输出问题 1)#包括 int main() { int a[5]={1,2,3,4,5}; int*ptr=(int*)(&a+1); printf(“%d%d”,*(a+1),*(ptr-1)); 返回0; },c,pointers,C,Pointers,输出为25&a表示a[0]的地址,因此&a+1应该是a[1]的地址。所以ptr应该包含a[1]的地址*(a+1)将为2,但*(ptr-1)也应为2。我不明白它是如何打印5的。&a将数组的地址作为数组指针,int(*)[5]。它是指向整个数组的指针类型,因此如果使用它执行指针算术,+1将意味着+sizeof(int[5]),这不是您想要的 正确代码: 1) #include <stdio.h> int main() { int a[5] = {1,2,3,4
&a
表示a[0]
的地址,因此&a+1
应该是a[1]
的地址。所以ptr
应该包含a[1]
的地址<代码>*(a+1)将为2,但*(ptr-1)
也应为2。我不明白它是如何打印5的。&a
将数组的地址作为数组指针,int(*)[5]
。它是指向整个数组的指针类型,因此如果使用它执行指针算术,+1
将意味着+sizeof(int[5])
,这不是您想要的
正确代码:
1) #include <stdio.h>
int main()
{
int a[5] = {1,2,3,4,5};
int *ptr = (int*)(&a+1);
printf("%d %d", *(a+1), *(ptr-1));
return 0;
}
值得注意的是,cast
(int*)
隐藏了这个bug。不要使用强制转换来消除您不理解的编译器错误 &a
不是a[0]
的地址,而是a
的地址。值可能相同,但类型不同。这对于指针算法来说很重要
在表达式&a+1
中,首先有&a
,其类型为int(*)[5]
,即指向大小为5的数组的指针。当您将1添加到该值时,它实际上会将sizeof(a)
字节添加到指针值。因此&a+1
实际上指向数组末尾后的一个字节。然后将此表达式从int(*)[5]
强制转换为int*
,并将其分配给ptr
然后,当您计算
*(ptr-1)
时,-
运算符从ptr
的字节值中减去1*sizeof(int)
,因此它现在指向数组的最后一个元素,即5,这就是打印的内容。这个表达式很重要:&a+1
。这实际上是(&a)+1
,它等于(&a)[1]
,它是指向数组末尾一个元素的指针
如果我们更“图形化”地看它,它看起来是这样的,并添加了相关的指针:
int *ptr = a+1;
+------+------+------+------+------+
|a[0]| a[1]| a[2]| a[3]| a[4]|
+------+------+------+------+------+
^ ^ ^
| | |
|&a[1](等于*(a+1))|
| |
&a[0](等于a)|
| |
&a&a+1
首先,&a
的类型是int(*)[5]
,因此对int*
的强制转换将打破严格的别名(这将导致未定义的行为)
其次,既然
ptr
有效地指向了a[5]
,那么ptr-1
将指向a[4]
首先,你说:&a
意味着a[0]
的地址,那么&a+1
应该是a[1]的地址吗?不,你错了&a
表示a
的地址,而不是a[0]
。而&a+1
意味着它按整个数组大小递增,而不仅仅是一个元素大小,a+1
意味着a[1]
的地址
这里
假设a
的基址为0x100
int a[5] = {1,2,3,4,5};
当你在做
--------------------------------------
| 1 | 2 | 3 | 4 | 5 |
--------------------------------------
0x100 0x104 0x108 0x112 0x116 ..
LSB
|
a
哪里ptr
点?首先执行(&a+1)
,它得到整个数组大小的增量,即
所以现在ptr
指向
(&a+1) == (0x100 + 1*20) /* &a+1 here it increments by array size */
== 0x120
现在,当你像
--------------------------------------
| 1 | 2 | 3 | 4 | 5 |
--------------------------------------
0x100 0x104 0x108 0x112 0x116 0x120
a |
ptr points here
这里
及
注意:-这里
*(ptr-1) == *(0x120 - 1*4)
== *(0x116) /* prints value at 0x116 memory location */
== 5
&a
的类型为int(*)[5]
即指向一个由5个元素组成的数组的指针,但您是按int*
类型强制转换的,正如@someprogrammerdude所指出的,它打破了严格的别名并导致未定义的行为
正确的一个是
int *ptr = (int*)(&a+1);
&a
指a
的地址,而不是a[0]
。a
的类型是int[5]
,因此&a+1
向该地址添加sizeof(a)
(即sizeof(int)*5
)。您希望使用int*ptr=(int*)(&a[0]+1)
使用a+1
或&a[0]+1
初始化您的ptr
,无需强制转换。如果ptr
的可能副本包含a[1]
,则*(ptr-1)
将是1
,而不是2
执行强制转换(int*)(&a+1)
中断严格别名,还是随后取消引用指针*(ptr-1)
可能会中断严格别名?@chux甚至没有尝试深入研究规范,我会说这是强制转换,因为它告诉编译器指针&a+1
实际上不是。但不要引用我的话!:)谢谢-嗯,在回顾时,我对&a+1
、(int*)(&a+1)
、和int*ptr=(int*)(&a+1)充满信心根据C11§6.3.2.3 7
对所有代码进行了明确定义。然而,这个小问题不应该让一个好的计划失去更多answer@Michi最好将这些问题作为问题而不是评论发布。强制转换从不违反严格的别名规则。规则是关于通过U类型的左值访问T类型的对象,其中T和U之间不存在某种关系。cast(int*)隐藏了这个错误
如果我更改int*ptr=(int*)(&a+1),会发生什么代码>到int*ptr=*(&a+1)代码>。因为看起来是一样的。现在ptr
指向int(*)[5]
并且没有强制转换。没有bug;代码正在尝试(并成功)显示a
的最后一个元素。“正确的代码”将显示第一个元素。@Michi您不能将int(*)[5]
隐式转换为int*
,它们是不兼容的指针类型<代码>int*ptr=*(&a+1)代码>是好的,除非它位于无处的中间。
printf("%d %d", *(a+1), *(ptr-1));
*(a+1) == *(0x100 + 1*4) /* multiplied by 4 bcz of elements is of int type*/
== *(0x104) /* value at 0x104 location */
== 2 (it prints 2)
*(ptr-1) == *(0x120 - 1*4)
== *(0x116) /* prints value at 0x116 memory location */
== 5
int *ptr = (int*)(&a+1);
int *ptr = a+1;