从堆栈中删除的函数内部变量在内存中如何表现
How Does Variables Inside Functions Which are Removed From The Stack Behave in Memory
我用 C:
写了这段代码
#include <stdio.h>
int* fun() {
int x = 43;
int* ret = &x;
return ret;
}
int main() {
int* r = fun();
printf("%d", *r);
return 0;
}
原代码为:
#include <stdio.h>
int* fun() {
int x = 43;
return &x;
}
int main() {
int* r = fun();
printf("%d", *r);
return 0;
}
我通过
修复了“Return本地指针”警告
int* ret = &x;
现在我的问题是,在第一个示例中,代码打印 43,我不明白为什么?在函数“fun”执行并从堆栈中删除后,指针不应该是指向垃圾值的指针,因为变量“x”被破坏并且它不再保存值 43 吗?那么为什么在“fun”执行后取消引用指针“r”会导致 43 呢?为什么即使在函数“fun”从堆栈中删除后,保存变量“x”的内存块仍然没有被删除?
纯属偶然。您正在调用 未定义的行为 以取消引用指向生命周期已结束的对象的指针。
当我将您的代码输入 Compiler Explorer 时,我得到了这个:
fun:
pushq %rbp
movq %rsp, %rbp
movl , -12(%rbp)
leaq -12(%rbp), %rax
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
popq %rbp
ret
.LC0:
.string "%d"
main:
pushq %rbp
movq %rsp, %rbp
subq , %rsp
movl [=10=], %eax
call fun
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
movl (%rax), %eax
movl %eax, %esi
movl $.LC0, %edi
movl [=10=], %eax
call printf
movl [=10=], %eax
leave
ret
在此图中,让我假设 %rsp = 0x10000000
就在执行 call fun
之前。
call
将 return 地址压入堆栈,因此它将从堆栈指针中减去 8。 push
栈指针也减8。因此,执行movl , -12(%rbp)
时,%rbp
为0x0ffffff0
,栈上布局为:
-12(%rbp) -> 0x0fffffe4 43
0x0fffffe8 (the address to return will be written here)
0x0fffffec (the address to return will be written here)
(%rbp) -> 0x0ffffff0 old base pointer %rbp
0x0ffffff4 old base pointer %rbp
0x0ffffff8 address to return from fun
0x0ffffffc address to return from fun
0x10000000
0x10000004
0x10000008 (returned address will be stored here in main())
0x1000000c (returned address will be stored here in main())
%rbp in main() -> 0x10000010
从 fun()
return 后,没有内存写入破坏 movl (%rax), %eax
读取值之前写入的 43
。因此,该值恰好被保留了。
再次注意,您正在调用未定义的行为,因此结果可能不同。
例如,当添加-O2
优化选项时,Compiler Explorer给了我this:
fun:
xorl %eax, %eax
ret
.LC0:
.string "%d"
main:
subq , %rsp
xorl %esi, %esi
movl $.LC0, %edi
xorl %eax, %eax
call printf
xorl %eax, %eax
addq , %rsp
ret
现在错误的函数 fun()
被替换为空进程,结果也改变了。
我用 C:
写了这段代码#include <stdio.h>
int* fun() {
int x = 43;
int* ret = &x;
return ret;
}
int main() {
int* r = fun();
printf("%d", *r);
return 0;
}
原代码为:
#include <stdio.h>
int* fun() {
int x = 43;
return &x;
}
int main() {
int* r = fun();
printf("%d", *r);
return 0;
}
我通过
修复了“Return本地指针”警告int* ret = &x;
现在我的问题是,在第一个示例中,代码打印 43,我不明白为什么?在函数“fun”执行并从堆栈中删除后,指针不应该是指向垃圾值的指针,因为变量“x”被破坏并且它不再保存值 43 吗?那么为什么在“fun”执行后取消引用指针“r”会导致 43 呢?为什么即使在函数“fun”从堆栈中删除后,保存变量“x”的内存块仍然没有被删除?
纯属偶然。您正在调用 未定义的行为 以取消引用指向生命周期已结束的对象的指针。
当我将您的代码输入 Compiler Explorer 时,我得到了这个:
fun:
pushq %rbp
movq %rsp, %rbp
movl , -12(%rbp)
leaq -12(%rbp), %rax
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
popq %rbp
ret
.LC0:
.string "%d"
main:
pushq %rbp
movq %rsp, %rbp
subq , %rsp
movl [=10=], %eax
call fun
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
movl (%rax), %eax
movl %eax, %esi
movl $.LC0, %edi
movl [=10=], %eax
call printf
movl [=10=], %eax
leave
ret
在此图中,让我假设 %rsp = 0x10000000
就在执行 call fun
之前。
call
将 return 地址压入堆栈,因此它将从堆栈指针中减去 8。 push
栈指针也减8。因此,执行movl , -12(%rbp)
时,%rbp
为0x0ffffff0
,栈上布局为:
-12(%rbp) -> 0x0fffffe4 43
0x0fffffe8 (the address to return will be written here)
0x0fffffec (the address to return will be written here)
(%rbp) -> 0x0ffffff0 old base pointer %rbp
0x0ffffff4 old base pointer %rbp
0x0ffffff8 address to return from fun
0x0ffffffc address to return from fun
0x10000000
0x10000004
0x10000008 (returned address will be stored here in main())
0x1000000c (returned address will be stored here in main())
%rbp in main() -> 0x10000010
从 fun()
return 后,没有内存写入破坏 movl (%rax), %eax
读取值之前写入的 43
。因此,该值恰好被保留了。
再次注意,您正在调用未定义的行为,因此结果可能不同。
例如,当添加-O2
优化选项时,Compiler Explorer给了我this:
fun:
xorl %eax, %eax
ret
.LC0:
.string "%d"
main:
subq , %rsp
xorl %esi, %esi
movl $.LC0, %edi
xorl %eax, %eax
call printf
xorl %eax, %eax
addq , %rsp
ret
现在错误的函数 fun()
被替换为空进程,结果也改变了。