【问题标题】:why the second printf prints garbage value为什么第二个 printf 打印垃圾值
【发布时间】:2014-01-05 17:01:45
【问题描述】:

这是源代码

#include <stdio.h>
#include <stdlib.h>

int *fun();

int main()
{  
    int *j;
    j=fun();
    printf("%d\n",*j);
    printf("%d\n",*j);
    return 0;
}

int *fun()
{
    int k=35;
    return &k;
}

输出-

35
1637778

第一个 printf() 打印出 35,这是 k 的值,但是

在 main() 中,第二个 printf 打印一个垃圾值而不是打印 35。为什么?

【问题讨论】:

标签: c garbage


【解决方案1】:

这里的问题是fun 的返回是返回一个局部变量的地址。该地址在函数返回的那一刻变得无效。第一次打电话给printf,你很幸运。

即使在fun 返回时本地在技术上被销毁,C 运行时也不会主动销毁它。因此,您第一次使用*j工作,因为本地的内存还没有被覆盖。 printf 的实现很可能只是通过在方法中使用它自己的本地变量来重写它。因此,在*j 的第二次使用中,您指的是使用的本地printf 而不是k

为了完成这项工作,您需要返回一个指向比fun 寿命更长的值的地址。通常在 C 中,这是通过 malloc

实现的
int *fun() {
  int* pValue = malloc(sizeof(int));
  *pValue = 23;
  return pValue;
}

因为malloc 的返回在您调用free 之前一直存在,这将在printf 的多次使用中有效。一个问题是调用函数现在必须通过fun 的返回来告诉程序何时完成。要在第二次调用printf 后调用free

j=fun();
printf("%d\n",*j);
printf("%d\n",*j);
free(j);

【讨论】:

  • 但是堆栈跟随 lifo ,所以 k 应该首先从堆栈中删除,所以 first printf() 如何给出正确的输出
  • @user3162531 通常,当从函数返回时,C 实际上并没有删除堆栈值。它只是调整堆栈指针以指向调用函数。在本地被另一段代码显式覆盖之前,该值通常仍然可以访问。永远不要依赖这种行为,它只是大多数实现的产物
  • 但是为什么第二个 printf() 覆盖了堆栈中的 k 呢?为什么不是第一个 printf()?
  • @user3162531 第一个 printf 覆盖了堆栈。表达式 *j 在运行 printf 之前进行评估。因此读取死本地的地址,然后 printf 运行并覆盖该值
  • @user3162531 另外请注意,根据*j 在参数列表中的位置,在推送第一个printf() 的参数时,它也可能被覆盖。所以永远不要依赖 UB。
【解决方案2】:

程序调用未定义的行为。您不能返回指向自动局部变量的指针。一旦fun 返回,该变量将不再存在。在这种情况下,您得到的结果可能是预期的或意外的。
永远不要返回指向自动局部变量的指针

【讨论】:

    【解决方案3】:

    您正在返回存储在堆栈中的本地值。当您退出功能时,它会被删除。你得到undefined behaviour

    在你的情况下stack 在函数返回后没有改变,所以你第一次得到正确的值。这在任何时候都不一样。

    【讨论】:

      【解决方案4】:

      两者都是错误的,因为您打印了一个不再存在的值:仅在函数执行时,在函数中存储int k 的内存是可以的;您不能返回对它的引用(指针),因为它将不再引用任何有意义的东西。

      以下方法将起作用:

      int *fun()
      {
        static int k=35;
      
        return &k;
      }
      

      静态关键字“说”即使函数没有运行,内存也必须“存活”,因此您返回的指针将是有效的。

      【讨论】:

        【解决方案5】:

        正如其他人已经说过的,您的程序调用了未定义的行为。

        这意味着,如果行为未定义,任何事情都可能发生。

        在您的情况下,会发生以下情况:返回位于堆栈上的变量的地址。从函数返回后,下一个函数调用可以 - 并且将 - 重用该空间。

        在错误地返回此地址的函数调用和使用该值的调用之间,没有任何反应 - 在您的情况下。请注意,即使在可能发生中断的系统上以及在具有能够中断正常程序运行的信号的系统上,这也可能有所不同。

        第一个printf() 调用现在将堆栈用于自己的目的——甚至可能是调用本身覆盖了旧值。所以第二个printf() 调用接收现在写入该内存的值。

        对于未定义的行为,任何事情都可能发生。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-02-18
          • 1970-01-01
          • 2016-04-15
          相关资源
          最近更新 更多