【问题标题】:Memory allocation stack内存分配栈
【发布时间】:2015-03-13 20:28:18
【问题描述】:

在堆栈中,内存是为main 保留的,我们将其称为main 函数的堆栈帧。

当我们调用Add 函数时,内存保留在栈顶。在Add 函数堆栈帧中,ab 是本地指针,c 是一个整数,它计算总和然后我们返回引用。 cAdd 函数的局部变量。

现在当Add函数执行完成时,堆栈中的内存空间也被释放,所以当我们尝试用指针p访问main中的这个地址时,我们试图访问的基本上是一个释放空间。编译器发出警告,但为什么它仍然正确打印值 5?

这个问题的答案可能是机器没有释放内存空间,因为它认为没有必要,因为没有更多的功能。但是,如果我们编写另一个函数Hello,那么它肯定应该在调用堆栈中为Add 函数释放空间,但程序仍然会打印

Yay     5

是因为像在堆中一样,我们需要在释放它之后分配一个指向null 的指针,否则我们仍然可以访问它?这里有类似的东西吗?

/* void Hello()
   {
     printf("Yay");
   } */

int* Add(int *a,int *b)
{
  int c=*a+*b;
  return &c;
}

int main()
{
  int a=1,b=4;
  int *p=Add(&a,&b);
  // Hello();
  printf("\t%d",*p);
  return 0;
}

【问题讨论】:

  • 这里的c 是什么? int c?
  • 我认为它必须是为了性能,内存被释放而不是归零,为什么会归零?有人对它为零感兴趣吗?如果您需要重用它,它只是可用,为什么不尝试使用相同的函数但将另一个变量地址传递给它,它可能会覆盖旧的,Hello() 函数小于Add() 函数。跨度>
  • 是的,它应该是 int c 和 Add。我编辑了那部分..
  • "是不是因为像在堆中一样,我们需要在释放它之后分配一个指向 null 的指针,否则我们仍然可以访问它?" 更改存储在指针中的值有对您是否可以访问它指向/指向的对象没有任何影响。

标签: c++ c pointers memory-management dangling-pointer


【解决方案1】:

假设c是本地变量int,函数调用后访问cundefined-behavior,可能会打印出预期的结果,也可能会做一些意想不到的事情。

虽然C 没有强制要求,但通常它是使用堆栈实现的。出于性能原因,当函数返回时,它会保持堆栈区域不变。这就是您看到值51+4 的原因。但你永远不应该指望这一点。


当您使用第二个函数时,行为仍然未定义,因此您可以获得任何输出。实际上,如果您在第二个函数中定义另一个变量并使用它,输出可能会发生变化。

                       +-----------------+
                       .(local variables).
                       |'''''''''''''''''|
                       |                 |
+----------------+     . PRINTF FUNCTION .
|  c = 42        |     |                 |
|''''''''''''''''|     +-----------------+
|                |     |                 |
.  ADD FUNCTION  .     . HELLO  FUNCTION .
|                |     |                 |
+----------------+     +-----------------+
|  b = 4         |     |  b = 4          |
|''''''''''''''''|     |'''''''''''''''''|
|  a = 1         |     |  a = 1          |
|''''''''''''''''|     |'''''''''''''''''|
|                |     |                 |
.  MAIN FUNCTION .     .  MAIN  FUNCTION .
|                |     |                 |
+----------------+     +-----------------+

在上图中,我试图直观地表示当您在 Add 函数和 Hello 函数中时堆栈的堆栈外观。可以看到Hello 并没有弄乱在Add 函数中为c 保留的堆栈内存。

您可以通过将Hello 函数重写为来验证这一点

void Hello()
{
  int i = 42;
  printf("Yay - %d\n", i);
}

可以在main 中打印42

【讨论】:

  • 你说当一个函数返回时它会保持堆栈区域不变,但是如果我们有另一个函数不应该释放 add 函数,因为我们在这里遵循 LIFO 所以在 Hello 函数之后,main 函数应该是活动的但是如果我们在调用堆栈中有 add 函数,那么按照 LIFO 原则,它怎么能到达 main 呢?
  • @Stack 补充了一些细节,希望有用。
  • @MohitJain 先生,最好的解释。 +1。 :-)
【解决方案2】:

TL;DR 回答,这是未定义的行为。您无法合理化上述执行的任何输出。如果它最终打印您的手机号码或邮政编码,请不要感到惊讶。 :-)

作用域到期后,堆栈位置会发生什么变化,取决于环境。如果不需要,分配给c 的堆栈空间可能不会被重用,因此您会看到假定正确 输出,但它仍然 未定义。总有一天,在不同的环境中,你可能会看到不同的答案。

Mr. Mothit Jainanswer对此给出了非常详细的解释。

【讨论】:

    【解决方案3】:

    我认为是因为当我运行以下程序时,您没有在 Hello 函数中定义“堆栈变量”:

    #include <stdio.h>
    
    void Hello()
    {
      int tt = 100;
      int tt1 = 5000;
      printf("Yay");
    }
    
    int* Add(int *a,int *b)
    {
      int c=*a+*b;
      return &c;
    }
    
    int main()
    {
      int a=1,b=4;
      int *p=Add(&a,&b);
      Hello();
      printf("\t%d",*p);
      return 0;
    }
    

    它在我的 Linux 中打印 5000 而在我的 Mac 中打印 1,无论如何我认为它们都是意想不到的价值。

    【讨论】:

      【解决方案4】:

      Hello 函数不会将任何新内容放入堆栈,因为它没有自动变量。因此,之前被c 占用的内存位置仍然没有改变。更改Hello 使其修改内存,您将更改p 的值。

      void Hello(void)
      {
          //This will overwrite the memory previously containing '5' with '3`, the number
          //of characters output by the printf() function.
          int grub = printf("Yay");
      }
      

      【讨论】:

        【解决方案5】:

        要完全理解这一点,我建议您学习处理器的汇编,并了解您的代码是如何编译的。在这一点上,我认为如果我说从函数返回不调用任何内存释放函数,它只是调整寄存器,并且由于在 Add 和 print 之间你没有做任何会改变“释放”堆栈上的值的事情,我认为这会有所帮助,你得到号码。尝试调用一个返回的函数.. pfft i dunno 77 介于两者之间(但请务必将其保存到局部变量!),您将打印不同的结果。学习汇编,做一个合适的程序员。

        【讨论】:

        • 但是我在 printf 之前包含了一个函数 Hello,所以不应该释放 add 函数,以便在堆栈中我们有 Hello,然后维护 main 和 LIFO?
        • 您现在可以从 Mohit 的回答中看出原因。您的 hello 函数只是没有到达该地址。实际上,这似乎不是 Mohit 的版本,我不认为,但是您是否会使用 Add 函数并将名称更改为 Hello 并使 c = 42 然后即使您返回 0 或任何您会在打印中得到 42
        猜你喜欢
        • 2018-07-24
        • 2014-10-26
        • 2021-11-18
        • 2021-04-08
        • 2014-01-02
        • 2011-05-28
        • 1970-01-01
        • 2011-01-06
        相关资源
        最近更新 更多