指针和数组混淆

use*_*850 7 c

我们有

 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)

应该是什么结果?

caf*_*caf 6

由于类型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)


Ale*_*lli 4

所有问题都来自于 的使用&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后面跟着两个完全任意的整数值。由于ptrt是指向 的指针,因此和int的地址算术缩放不会补偿在 上完成的操作(内存地址的缩放是通过,而不是!),因此和分别指向(据称;-)s “结束后几秒”和“开始前几秒”。-1+1&asizeof(int)sizeof(a)ptr-1t+1intintainta

无法知道在这些任意地址处是否存在允许进程寻址的任何内存(因此可能存在分段违规),并且如果存在任何可访问的内存则其“被视为int”的内容可能是什么。

编辑:@caf 指出这ptr - 1不是无效——它正确地指向a;的最后一个元素。因此输出(除非存在分段错误,@NullUserException 认为这是不太可能的,但在这一点上我们不同意;-)将从20 50第三个“任意”垃圾之前开始。要点是,根据 C 标准,计算(尽管不使用)数组“仅超出末尾”的指针是有效的,并且数组的大小必须恰好等于该数组的长度乘以其元素的大小(填充)如果需要的话,元素的类型是允许的,如果是这样,它会显示在元素自己的 sizeof 中,但不适用于整个数组)。微妙,但重要;-)。

  • 顺便说一下,“*(ptr - 1)”是可以的——它指向“a”中的最后一个“int”。不过,“*(t + 1)”是垃圾。 (2认同)
  • @Alex 只是一些小修正:(1) `*(a+1)` 是 20,而不是 10 (2) `*(ptr-1)` 保证指向 `a[4]`,(3 ) `*(t+1)` 指向 `a[-4]`,这是垃圾,但不太可能导致程序出现段错误。 (2认同)