我们有
int a[5]={10, 20, 30, 40, 50};
Run Code Online (Sandbox Code Playgroud)
我想知道以下两个代码段是如何做的?
int *ptr = (int *)(&a+1);
int *t = (int *)(&a -1);
Run Code Online (Sandbox Code Playgroud)
如果我们有
printf("%d %d %d \n", *(a+1), *(ptr-1), *(t+1));
Run Code Online (Sandbox Code Playgroud)
应该是什么结果?
由于类型a是阵列的-5- int小号,这意味着,类型&a是指针到阵列的-5- int小号.
当您从指针添加或减去1时,您要求它指向内存中该类型的下一个或上一个对象.所以&a+1是创建一个指针阵列的-5-的int后立即a在存储器(不存在),并且&a-1是建立一个指针阵列的-5- int立即之前a在存储器中(其也不存在) .在内存中,它看起来像这样(每个单元格代表一个int):
Address: &a-1 &a &a+1
Contents: | ? | ? | ? | ? | ? | 10 | 20 | 30 | 40 | 50 | ? | ? | ? | ? | ? |
Run Code Online (Sandbox Code Playgroud)
当a在表达式中使用*(a+1),它被转换成一个指针到它的第一个元素-所以一个指针TO- int在指示10值.添加一个然后使指针指向下一个int- a+1指向该20值的点. *(a+1)然后获取该值,因此打印的第一个数字是20.
因为ptr它也是一个指针int,这意味着ptr - 1创建一个指向int前一个指针ptr- 在这种情况下,它将指向50.所以打印的第二个数字是50.
同样,t + 1创建一个指向int紧随其后的指针t- 在这种情况下,它?是上图中的第二个.这是一个未初始化的值 - 它可以打印任何内容,甚至可以使程序崩溃.
Address: &a-1 &a &a+1
t t+1 a a+1 ptr-1 ptr
Contents: | ? | ? | ? | ? | ? | 10 | 20 | 30 | 40 | 50 | ? | ? | ? | ? | ? |
Run Code Online (Sandbox Code Playgroud)
所有问题都来自于 的使用&a,它是一个指向“五个整数的数组”的指针,因此指针算术(当您考虑地址时)会“缩放” (如果是 4 个字节sizeof(a),则可能为 20)int并且编译器不需要出于对齐目的进行填充——这是合理的假设,尽管当然还远未确定。
所以,之后
int *ptr = (int *)(&a+1);
int *t = (int *)(&a -1);
Run Code Online (Sandbox Code Playgroud)
ptr是一个指向内存地址“sizeof(a)大于地址a”的int的指针,t对于“sizeof(a)小于a的地址”也是类似的。所以...:
printf("%d %d %d \n", *(a+1), *(ptr-1), *(t+1));
Run Code Online (Sandbox Code Playgroud)
结果应该是什么?
很可能是分段违规,否则20后面跟着两个完全任意的整数值。由于ptr和t是指向 的指针,因此和int的地址算术缩放不会补偿在 上完成的操作(内存地址的缩放是通过,而不是!),因此和分别指向(据称;-)s “结束后几秒”和“开始前几秒”。-1+1&asizeof(int)sizeof(a)ptr-1t+1intintainta
无法知道在这些任意地址处是否存在允许进程寻址的任何内存(因此可能存在分段违规),并且如果存在任何可访问的内存,则其“被视为int”的内容可能是什么。
编辑:@caf 指出这ptr - 1不是无效的——它正确地指向a;的最后一个元素。因此输出(除非存在分段错误,@NullUserException 认为这是不太可能的,但在这一点上我们不同意;-)将从20 50第三个“任意”垃圾之前开始。要点是,根据 C 标准,计算(尽管不使用)数组“仅超出末尾”的指针是有效的,并且数组的大小必须恰好等于该数组的长度乘以其元素的大小(填充)如果需要的话,元素的类型是允许的,如果是这样,它会显示在元素自己的 sizeof 中,但不适用于整个数组)。微妙,但重要;-)。
| 归档时间: |
|
| 查看次数: |
370 次 |
| 最近记录: |