内存分配堆栈

Memory allocation stack

本文关键字:堆栈 分配 内存      更新时间:2023-10-16

在堆栈中,内存是为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,则undefined-behavior函数调用后访问c,并且可能会打印预期的结果,或者可能会执行意外操作。

虽然C不是强制性的,但通常它是使用堆栈实现的。出于性能原因,当函数返回时,它将保持堆栈区域不变。这就是为什么您看到5值的原因,即 1+4 .但你永远不应该指望这一点。


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

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

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

您可以通过将Hello函数重写为

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

可以在main打印42

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

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

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

Mothit Jain先生的回答对此给出了非常详细的解释。

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");
}
我认为

这是因为当我运行以下程序时,您没有在 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 个,无论如何我认为它们都是意想不到的价值。