内存分配堆栈
Memory allocation stack
在堆栈中,内存是为main
保留的,我们称之为main
函数的堆栈帧。
当我们调用 Add
函数时,内存保留在堆栈顶部。在Add
函数堆栈帧中,a
和b
是局部指针,c
是一个整数,它计算总和,然后我们返回引用。 c
是Add
函数的局部变量。
现在,当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 个,无论如何我认为它们都是意想不到的价值。
- 从堆栈分配的原始指针构造智能指针
- 在什么情况下,两个堆栈分配的结构对象的 this 点指向同一个地址?
- 如何模板化堆栈分配的多态指针数组到接口,包括派生类型的相应点?
- 堆栈分配的类类型.为什么两个 ID 实例的地址相同?
- C++析构函数调用两次,堆栈分配的复合对象
- 了解通过引用传递取消引用指针时C++堆/堆栈分配
- C++,在对象内分配多个数据时,堆栈分配是否更有效? 在下面的程序中,类A_Heap的效率会更低吗?
- 何时在函数中声明堆栈分配变量?
- 使用 std::map 的递归堆栈分配如何工作?
- tcmalloc 与纯堆栈分配性能有多接近
- 安全分配堆栈分配的阵列
- C++ 中的黑白堆分配对象和堆栈分配对象的性能差异
- 跟踪(堆栈分配)对象
- 堆栈分配的确切时间
- 未初始化的值是由堆栈分配 - Qt - C++创建的
- 用于堆栈分配对象的C++虚拟析构函数内联
- C STD :: BAD_ALLOC来自堆栈分配
- 是使用COM创建的对象,将采用分配或堆栈分配的内存
- C++ 中针对大型的堆栈分配
- 堆栈分配的向量如何在 C++ 中扩展