当本地函数的内存在堆栈上被清除时?
when the memory is cleared on stack for a local function?
我想知道分配给本地函数调用的堆栈中的内存究竟何时被清除。我在一些视频教程中看到,当函数调用返回到 main 时,分配给本地函数的内存被清除。我对下面的程序有几个问题,请解释。
#include<stdio.h>
void print(){
printf("testing \n");
}
int* sum(int* a, int* b){
int c = *a + *b;
return &c;
}
int main(){
int a=3,b=2;
int *ptr = sum(&a,&b);
print();
printf("sum is: %d",*ptr);
return 0;
}
当我运行上面的程序时,它正在打印预期的垃圾值。但是,如果我在 main 中注释 "print()" 函数,然后在程序中注释 运行 它正在打印正确的总和值。
这是否意味着即使本地函数在栈中执行完成,直到有另一个函数调用栈,之前分配的内存不会被清除?
如果我删除 "print()" 中的 "printf" 语句并保留 main 中的 "print()" 调用,那么我可以正常看到求和结果。为什么它没有覆盖堆栈中的内存?
Chas没有堆栈,标准中甚至没有提到堆栈这个词(C89,C99orC11)。 实现 可以使用堆栈来提供 C 抽象机的行为方面,但标准指定的是抽象机本身。
因此,至于何时清除堆栈(假设它甚至存在),这完全取决于实现。您所做的基本上是未定义的行为,在对象的生命周期结束后访问对象,因此结果可以是实现选择的任何内容。
至于为什么您可以在 特定 实现的生命周期结束后访问这些项目,很可能是因为进入和退出函数 不会 清除堆栈,它只是调整堆栈指针(比必须清除内存更有效)。
因此,除非有什么东西覆盖了该内存位置的内容(例如对 printf
的后续调用),否则它可能会保留在上次设置的位置。
例如,这里有一个函数的示例序言代码:
push ebp ; Save the frame pointer.
mov ebp, esp ; Set frame pointer to current stack pointer.
sub esp, XX ; Allocate XX space for this frame.
及其等效的结尾:
mov esp, ebp ; Restore stack pointer.
pop ebp ; Get previous frame pointer.
ret ; Return.
请注意,space 的分配(序言中的 sub
)和它的释放(结语中的mov
)实际上都清除 它正在使用的内存。
但是,如前所述,这不是您应该依赖的东西。
您问题的答案是特定于操作系统的。在从头开始创建进程的系统中 (VMS/NT),只有在创建进程时才会清除堆栈。堆栈是从需求零页面创建的。当第一次访问堆栈页时,操作系统会创建一个新的零页。
在分叉系统中,每当加载新的可执行文件时,堆栈就会被清除。通常过程同上。
创建堆栈后,放在那里的任何内容都会保留在那里,直到被覆盖。
堆栈由操作系统管理;不是编程语言。
我想知道分配给本地函数调用的堆栈中的内存究竟何时被清除。我在一些视频教程中看到,当函数调用返回到 main 时,分配给本地函数的内存被清除。我对下面的程序有几个问题,请解释。
#include<stdio.h>
void print(){
printf("testing \n");
}
int* sum(int* a, int* b){
int c = *a + *b;
return &c;
}
int main(){
int a=3,b=2;
int *ptr = sum(&a,&b);
print();
printf("sum is: %d",*ptr);
return 0;
}
当我运行上面的程序时,它正在打印预期的垃圾值。但是,如果我在 main 中注释 "print()" 函数,然后在程序中注释 运行 它正在打印正确的总和值。
这是否意味着即使本地函数在栈中执行完成,直到有另一个函数调用栈,之前分配的内存不会被清除?
如果我删除 "print()" 中的 "printf" 语句并保留 main 中的 "print()" 调用,那么我可以正常看到求和结果。为什么它没有覆盖堆栈中的内存?
Chas没有堆栈,标准中甚至没有提到堆栈这个词(C89,C99orC11)。 实现 可以使用堆栈来提供 C 抽象机的行为方面,但标准指定的是抽象机本身。
因此,至于何时清除堆栈(假设它甚至存在),这完全取决于实现。您所做的基本上是未定义的行为,在对象的生命周期结束后访问对象,因此结果可以是实现选择的任何内容。
至于为什么您可以在 特定 实现的生命周期结束后访问这些项目,很可能是因为进入和退出函数 不会 清除堆栈,它只是调整堆栈指针(比必须清除内存更有效)。
因此,除非有什么东西覆盖了该内存位置的内容(例如对 printf
的后续调用),否则它可能会保留在上次设置的位置。
例如,这里有一个函数的示例序言代码:
push ebp ; Save the frame pointer.
mov ebp, esp ; Set frame pointer to current stack pointer.
sub esp, XX ; Allocate XX space for this frame.
及其等效的结尾:
mov esp, ebp ; Restore stack pointer.
pop ebp ; Get previous frame pointer.
ret ; Return.
请注意,space 的分配(序言中的 sub
)和它的释放(结语中的mov
)实际上都清除 它正在使用的内存。
但是,如前所述,这不是您应该依赖的东西。
您问题的答案是特定于操作系统的。在从头开始创建进程的系统中 (VMS/NT),只有在创建进程时才会清除堆栈。堆栈是从需求零页面创建的。当第一次访问堆栈页时,操作系统会创建一个新的零页。
在分叉系统中,每当加载新的可执行文件时,堆栈就会被清除。通常过程同上。
创建堆栈后,放在那里的任何内容都会保留在那里,直到被覆盖。
堆栈由操作系统管理;不是编程语言。