【问题标题】:strange output issue in cc中的奇怪输出问题
【发布时间】:2018-06-26 12:43:31
【问题描述】:
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;
   }

输出为 2 5&amp;a 表示a[0] 的地址,所以&amp;a+1 应该是a[1] 的地址。所以ptr 应该是a[1] 的地址。 *(a+1) 将是 2,但 *(ptr-1) 也应该是 2。我不明白它是如何打印 5。

【问题讨论】:

  • &amp;a 表示a 的地址,而不是a[0]a 的类型是int[5],所以&amp;a + 1sizeof(a)(即sizeof(int) * 5)添加到该地址。你想使用int *ptr = (int*)(&amp;a[0] + 1);
  • a+1&amp;a[0] + 1初始化你的ptr,不需要强制转换。
  • 如果ptr 确实拥有a[1] 的地址,那么*(ptr - 1) 将是1,而不是2

标签: c pointers


【解决方案1】:

这个表达式很重要:&amp;a+1。这实际上是(&amp;a)+1,它等于(&amp;a)[1],它是指向数组末尾后一个元素的指针。

如果我们更“图形化”地看它,它看起来像这样,并添加了相关的指针:

+------+------+------+------+------+ |一个[0] |一个[1] |一个[2] |一个[3] |一个[4] | +------+------+------+------+------+ ^ ^ ^ | | | | &a[1] (等于 *(a + 1)) | | | &a[0] (等于 a) | | | &a &a+1

首先,&amp;a 的类型是int (*)[5],因此您对int * 的强制转换将破坏严格的别名(这会导致未定义的行为)。

其次,由于ptr 有效地指向a[5],那么ptr - 1 将指向a[4]

【讨论】:

  • 强制转换(int*)(&amp;a+1) 是否会破坏严格的别名,或者是后续取消引用指针*(ptr-1) 可能会破坏严格的别名?
  • @chux 甚至没有尝试深入研究规范,我会说它是演员表,因为它告诉编译器指针 &amp;a+1 确实不是。但不要引用我的话! :)
  • 谢谢 - 嗯,经过审查,我相信 &amp;a+1(int*)(&amp;a+1)int *ptr = (int*)(&amp;a+1); 都根据 C11 §6.3.2.3 7 进行了很好的定义。然而,这个小问题不应该从一个好的答案中拿走更多的东西
  • @Michi 最好发布诸如 questions 之类的问题,而不是 cmets。
  • 强制转换永远不会违反严格的别名规则。该规则是关于通过 U 类型的左值访问 T 类型的对象,其中 T 和 U 之间不存在某种关系。
【解决方案2】:

&amp;a不是a[0]的地址,而是a的地址。值可能相同,但 types 不同。当涉及到指针运算时,这一点很重要。

在表达式&amp;a + 1 中,您首先拥有&amp;a,其类型为int (*)[5],即指向大小为5 的数组的指针。当您向其添加1 时,它实际上将sizeof(a) 字节添加到指针值.所以&amp;a + 1 实际上指向数组末尾的一个字节。然后将此表达式从 int (*)[5] 转换为 int * 并将其分配给 ptr

当您随后计算 *(ptr - 1) 时,- 运算符从 ptr 的字节值中减去 1 * sizeof(int),因此它现在指向数组的最后一个元素,即 5,这就是打印出来的。

【讨论】:

    【解决方案3】:

    &amp;a 将数组的地址作为 数组指针int (*)[5]。它是一个指向整个数组的指针类型,所以如果你用它做指针运算,+1 将意味着+sizeof(int[5]) 这不是你想要的。

    正确代码:

    int *ptr = a+1;
    

    值得注意的是,演员 (int*) 隐藏了这个错误。不要使用强制类型转换来消除您不理解的编译器错误!

    【讨论】:

    • the cast (int*) was hiding this bug 如果我将int *ptr = (int*)(&amp;a+1); 更改为int *ptr = *(&amp;a+1);,会发生什么。因为它似乎是一样的。现在 ptr 指向 int (*)[5] 并且没有 Cast。
    • 没有bug;该代码正在尝试(并成功)显示a 的最后一个元素。您的“正确代码”将显示第一个元素。
    • @Michi 您不能从int (*)[5] 隐式转换为int*,它们不是兼容的指针类型。 int *ptr = *(&amp;a+1); 很好,只是它指向不知名的地方。
    • @M.M 目前还不清楚意图是什么。然而,很明显&amp;a+1 指向数组末尾之外,而不是最后一个元素。 int*int (*)[5] 不兼容,它们也不一定是别名。我不认为对数组指针进行指针运算然后转换为int* 是明确定义的行为。我们可以在不调用 UB 的情况下将 1 项指向数组之外,但在这种情况下,它最终会超出 1 个数组,而不是 1 个 int。
    【解决方案4】:

    首先,您说:&amp;a 表示a[0] 的地址,所以&amp;a+1 应该是a[1] 的地址? 不,你错了。 &amp;a 表示a 的地址,而不是a[0]&amp;a+1 表示它以整个数组大小递增,而不仅仅是一个元素大小,a+1 表示a[1] 的地址。

    这里

    int a[5] = {1,2,3,4,5};
    

    假设a 的基地址是0x100

        --------------------------------------
        |   1   |   2   |   3  |   4   |   5  |
        --------------------------------------
       0x100   0x104   0x108   0x112  0x116 ..  
       LSB
        |
        a  
    

    当你在做的时候

    int *ptr = (int*)(&a+1);
    

    ptr 点在哪里?第一次执行(&amp;a+1),它得到整个数组大小的增量,即

    (&a+1) == (0x100 + 1*20) /* &a+1 here it increments by array size */
           == 0x120
    

    所以现在ptr 指向

        --------------------------------------
        |   1   |   2   |   3  |   4   |   5  |
        --------------------------------------
       0x100   0x104   0x108   0x112  0x116  0x120  
        a                                     |
                                             ptr points here
    

    现在当你像打印一样

    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);
    

    &amp;a 的类型是 int(*)[5] 即指向 5 个元素的数组的指针,但您正在转换为 @someprogrammerdude 所指出的 int* 类型,它打破了严格的别名并导致未定义的行为。

    正确的是

    int *ptr = a+1;
    

    【讨论】:

      猜你喜欢
      • 2021-12-03
      • 2011-06-14
      • 2020-06-10
      • 2011-11-19
      • 1970-01-01
      • 1970-01-01
      • 2016-02-19
      • 1970-01-01
      • 2023-01-29
      相关资源
      最近更新 更多