【问题标题】:How Does Variables Inside Functions Which are Removed From The Stack Behave in Memory从堆栈中删除的函数内部的变量如何在内存中表现
【发布时间】:2021-07-15 13:16:16
【问题描述】:

我用 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;
}

我通过这样做修复了“返回本地指针”警告

int* ret = &x;

现在我的问题是,在第一个示例中,代码打印 43,我不明白为什么?在函数“fun”执行并从堆栈中删除后,指针不应该是指向垃圾值的指针,因为变量“x”被破坏并且不再保存值 43 吗?那么为什么在“fun”执行后取消引用指针“r”会导致 43 ?为什么这个包含变量“x”的内存块即使在函数“fun”从堆栈中移除后仍然没有被移除?

【问题讨论】:

  • 43-7652342 或任何其他数字一样多:-)
  • 这是未定义的行为。你只是幸运。内存是按页保护的。它仍然存在,但可能会导致段错误。
  • 堆栈内存永远不是removed - 它只是为下一步需要的任何东西提供(不变)。
  • 从技术上讲,内存位置已被释放,但还没有人向其写入任何内容,因此它仍保持先前的值。但这是未定义的行为,在其他情况下您可能会得到完全不同的值。

标签: c pointers memory memory-management


【解决方案1】:

这是偶然的。您正在调用 未定义的行为 来取消引用指向生命周期已结束的对象的指针。

当我将您的代码输入Compiler Explorer 时,我得到了这个:

fun:
        pushq   %rbp
        movq    %rsp, %rbp
        movl    $43, -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    $16, %rsp
        movl    $0, %eax
        call    fun
        movq    %rax, -8(%rbp)
        movq    -8(%rbp), %rax
        movl    (%rax), %eax
        movl    %eax, %esi
        movl    $.LC0, %edi
        movl    $0, %eax
        call    printf
        movl    $0, %eax
        leave
        ret

在这个插图中,让我假设 %rsp = 0x10000000 在执行 call fun 之前。

call 将返回地址压入堆栈,因此它会从堆栈指针中减去 8。 push 也从堆栈指针中减去 8。因此,movl $43, -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()返回后,没有内存写入破坏43在读取movl (%rax), %eax之前写入的值。因此,该值恰好被保留了。

再次注意,您正在调用未定义的行为,因此结果可能会有所不同。

例如,当添加-O2优化选项时,编译器资源管理器给了我this

fun:
        xorl    %eax, %eax
        ret
.LC0:
        .string "%d"
main:
        subq    $8, %rsp
        xorl    %esi, %esi
        movl    $.LC0, %edi
        xorl    %eax, %eax
        call    printf
        xorl    %eax, %eax
        addq    $8, %rsp
        ret

现在buggy函数fun()被替换为空进程,结果也改变了。

【讨论】:

    猜你喜欢
    • 2017-05-18
    • 1970-01-01
    • 2018-05-22
    • 1970-01-01
    • 2012-03-04
    • 1970-01-01
    • 2014-12-23
    • 2017-10-21
    • 2020-01-03
    相关资源
    最近更新 更多