从堆栈中删除的函数内部变量在内存中如何表现

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)时,%rbp0x0ffffff0,栈上布局为:

     -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() 被替换为空进程,结果也改变了。