【问题标题】:Why this print statement changes the previous pointer?为什么这个 print 语句改变了前一个指针?
【发布时间】:2013-03-12 16:20:59
【问题描述】:

这是程序:

#include <stdio.h>

main() {
  int * i;
  int * j;
  printf("%d\n", i);
  printf("%d\n", j);
}

我编译运行,结果是:

888086464
0

但是,如果我注释掉第二个 printf

#include <stdio.h>

main() {
  int * i;
  int * j;
  printf("%d\n", i);
  //printf("%d\n", j);
}

输出beomes:

0

我想知道为什么第二个 printf 会更改指针 i。
而且,C 是如何初始化指针的?据我所知,如果指针未初始化,它的值 Null 将等于 0,对吗?为什么在第一个输出中, i 被初始化了?

编辑:根据你们的建议,我做了一些更改:

#include <stdio.h>

main() {
  int * i;
  int * j;
  printf("%p\n", i);
  printf("%p\n", j);
}

这给了

0x7fff67a361b0
(nil)

但是当我注释掉第二个 printf 时

#include <stdio.h>

main() {
  int * i;
  int * j;
  printf("%p\n", i);
  //printf("%p\n", j);
}

输出为零。 我很好奇为什么在有两个 printf 的第一个版本中,只有 j 是 nil 而 i 不是。

【问题讨论】:

  • 除了使用未初始化的数据(未定义)之外,您还使用 %d 格式传递指向 printf() 的指针,这也是未定义的。最好使用%p 或从指针显式转换为int

标签: c pointers printf


【解决方案1】:

您没有初始化任何一个指针。您正在查看的是未定义的行为。您看到的值只是堆栈中的垃圾值,每次运行时都可能发生变化。

【讨论】:

  • 为什么 i 指向一个垃圾值,而 j 没有?在第一个输出中。
  • 它们都是垃圾值。仅仅因为其中一个恰好是0 并不意味着它仍然不是垃圾。
  • 但是每次运行,秒总是0,我觉得不是巧合吧?
  • @octref 这绝对是巧合。碰巧你没有运行其他任何东西,所以当你初始化这些变量时,堆栈总是看起来一样。如果你尝试做一些其他的事情(比如预先运行其他函数),那么它们的值可能会改变。
  • @octref:不,这不仅仅是巧合,而是由您的计算环境的其他方面而不是 C 标准引起的行为。特别是,你没有控制这些方面,所以你不能依赖那里总是一个零。通过充分调查,您可能会发现为什么那里有一个零,这可能是代码初始化环境以准备启动 C 程序的某种结果。但答案不会在 C 语义中。
【解决方案2】:

如果你没有初始化指针,它可以有任何值。

必须初始化它们。否则你会得到一个undefined behavior

指针打印的正确方法是:

printf("%p", pointer);

【讨论】:

    【解决方案3】:

    您可以通过查看为两个版本生成的机器代码来回答您的问题(对于 gcc,这应该是 -S 选项)。

    怀疑在第二个版本中,j 根本没有被创建(因为它没有在任何地方使用),所以 i 是在插槽中创建的将用于j。无论出于何种原因,您的堆栈设置为使前 64 位为0x0000000000000000,后跟0x00007fff67a361b0

    IOW,在第一个版本中,您的堆栈看起来像

     Item        Address            00  01  02  03  04  05  06  07   
     ----        -------            --  --  --  --  --  --  --  --
        j        0x8000             00  00  00  00  00  00  00  00
        i        0x8008             00  00  7f  ff  67  a3  61  b0
    

    而在第二个版本中,它看起来像

     Item        Address            00  01  02  03  04  05  06  07   
     ----        -------            --  --  --  --  --  --  --  --
        i        0x8000             00  00  00  00  00  00  00  00
                 0x8008             ??  ??  ??  ??  ??  ??  ??  ??
    

    (地址值仅用于说明,不对应任何实际架构)。

    为了咯咯笑,将打印语句修改为

    printf("%p: %p\n", &i, i);
    printf("%p: %p\n", &j, j);
    

    在第一个版本中

    printf("%p: %p\n", &i, i);
    

    在第二个。我敢打赌,第二版中为&amp;i(变量i 的地址)打印的值将与为&amp;j(变量j 的地址)打印的值相同。第一个版本。

    注意 - 这与 C 语言 无关,与您的特定实现(编译器、链接器等)有关。我不相信您正在调用未定义的行为(您没有通过那些无效指针访问任何内存),但是您看到了不显式初始化指针值的危险。

    在块范围内声明的没有static 关键字的变量不会被初始化;实例化变量时内存中的任何内容都是初始值,并且该位模式可能不代表该类型的有效值(这称为陷阱表示)。在文件范围(在任何函数之外)或使用 static 关键字声明的变量初始化为 0 或 NULL,具体取决于它是标量类型还是指针类型。聚合类型(数组、结构和联合)的规则稍微复杂一些,但基本原理是相同的。

    【讨论】:

    • 这正是我要找的解释,非常感谢!
    • 我刚刚意识到,除了第一个元素全为 0 之外,没有理由让两次运行之间的堆栈相同。您可能需要运行一个调试器来检查两个运行的堆栈版本。但同样,这完全是关于你的具体实现,而不是一般的 C。无法保证相同的代码在使用不同的编译器时会以相同的方式运行。
    【解决方案4】:

    “未初始化”与“初始化为 0”(或 NULL)非常不同。

    变量ij 未初始化。他们留下了记忆中的东西。

    当您注释掉第二个printf 时,变量j 不再使用。它可能已被编译器优化(意味着根本没有定义)。在这种情况下,i 可能位于不同的位置,具有不同的 未初始化 值。

    这实际上是未定义的行为。我的最后一部分纯属猜测,你永远无法确定。

    【讨论】:

    • 但是有两个 printf,其中 i 和 j 都用了,为什么一个是 nil 而一个有地址呢?
    【解决方案5】:

    指针可以容纳任何东西。除非您告诉它,否则 C 不会初始化本地(自动)变量。如果您将它们定义为静态,它们将被初始化为 NULL。

    您还应该使用%p 来打印指针,而不是%d

    【讨论】:

      【解决方案6】:

      C 只会自动初始化static 和全局变量(全局变量在函数的外部 声明)为零。 自动 变量(这就是你的两个指针的调用)不会自动初始化,所以你正在打印“堆栈噪音”(即当你的函数初始化时,堆栈上发生的任何值。打印的值甚至可能会在程序的运行过程中发生变化。如果您希望将它们初始化为NULL,您需要告诉编译器:

      int *i = NULL;
      int *j = NULL;
      

      【讨论】:

        【解决方案7】:

        如果你不初始化指针地址,你会得到未定义的行为

        在这里您将指针地址打印为 int,这是不正确的,您应该使用 "%p" 而不是 "%d"

        【讨论】:

        • %u%x 期望 unsigned int%d 一样错误。
        • @EricPostpischil 访问不确定的内存会导致 C90 中的未定义行为,并且无论最新标准的确切措辞如何,现代编译器都将其视为未定义行为。 kqueue.org/blog/2012/06/25/more-randomness-or-less
        • @EricPostpischil (1) 1.6 术语定义 [...] 未定义行为 --- 使用不可移植或错误程序构造、错误数据或不确定值对象时的行为,[... ]
        • @EricPostpischil 如果你对“could have been declared with the register storage class”这句话的故事感兴趣,缺陷报告在open-std.org/jtc1/sc22/wg14/www/docs/dr_338.htm
        • @EricPostpischil 一个不确定的值可能是一个陷阱表示,然后使用它会中止执行。您是否在考虑未指定的值?
        【解决方案8】:

        And, how does C initialize pointers?

        C 没有,你做。

        As far as I know, if a pointer is not initialized, it would have value Null which is equal to 0, correct?

        错了。如果它没有被初始化,它就没有定义的值。会是垃圾。打印未初始化的变量将导致垃圾,这就是您无法预期输出的原因。

        Why in the first output, the i was initialized?

        不是,只是碰巧指向 0。

        附注:当您打印指针时,您应该使用%p 进行打印。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-02-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-04
          • 1970-01-01
          • 2019-05-15
          • 2021-01-16
          相关资源
          最近更新 更多