C++ 在变量的内存地址上做了啥来“释放”它?

Posted

技术标签:

【中文标题】C++ 在变量的内存地址上做了啥来“释放”它?【英文标题】:What does C++ do at the memory address of a variable to "deallocate" it?C++ 在变量的内存地址上做了什么来“释放”它? 【发布时间】:2019-02-07 19:07:33 【问题描述】:

例如。 当一个具有局部整数变量 x 的函数结束时,C++ 对存储在与 x 对应的内存位置的值做了什么? 是否插入随机值?

【问题讨论】:

没有专门做的。为什么你需要知道? 销毁涉及对象清理(这是内置类型的无操作)和释放。你问的是销毁还是释放。 这是定义的实现,而不是你程序员可以知道的事情(也不是你应该需要关心的事情)。从你的角度来看,它已经消失了。 答案:实现细节,可能因平台而异。可能是;实现细节。 该值被忽略并最终可能被覆盖。局部变量可以存储在寄存器中。如果它们存储在寄存器中,则很可能在重用寄存器时会覆盖该值。 【参考方案1】:

可能发生的事情 - 什么都没有。删除某些东西会占用资源,因此它只是调整指向该内存的堆栈指针。这将导致下次使用此内存时将其覆盖。

您可以在不初始化变量的情况下使用变量时看到类似的情况。

int i;

i 将在其中包含“垃圾”数据,该“垃圾”来自内存中此特定位置正在使用的时间。可以是旧照片、文本文件或 w/e。

【讨论】:

注意:局部变量可以在寄存器中分配。因此不会分配任何堆栈内存。【参考方案2】:

这在很大程度上取决于实现。

在大多数情况下,什么都不做。您解除分配(这会更改堆栈指针)。在这种情况下,您无需修改​​内存。

在一些非常晦涩的情况下,您可能会发现不同的事情。在一个示例中,可以将 RTOS RTEMS 设置为在解除分配时写入内存。它可能会写一些特定的东西,比如0xCDCDCDCD0xDEADBEEF。这在调试这些 RTOS 系统中的内存问题时会很有帮助,因为当您使用坏内存时很容易识别。但这非常罕见

【讨论】:

使用“将内存标记为未使用”这样的措辞具有误导性。没有标记,至少对于函数结束时生命周期结束的单个对象没有标记。在典型的实现中,发生的所有事情(对于普通旧数据)都是调整堆栈指针。【参考方案3】:

理论上没有指定,但实际上不会为单个基本类型对象(如 int、double、T* 等)进行分配。在某些情况下,堆栈指针不会被修改,编译器只是重用其他变量的空间:

for (int i=0 ; i < 10 ; i++)
   foo(i);
for (int j=0 ; j < 10 ; j++)
   foo(j);

最有可能的是,编译器将重用i 的空间来分配j。这在godbolt.org with gcc 上很容易验证:

.L2:
        mov     edi, ebx
        add     ebx, 1
        call    foo(int)
        cmp     ebx, 10
        jne     .L2
        xor     ebx, ebx
.L3:
        mov     edi, ebx
        add     ebx, 1
        call    foo(int)
        cmp     ebx, 10
        jne     .L3

不仅循环是相同的,它们甚至不使用堆栈。而不是堆栈,ij 变量都分配在 `ebx.在这个例子中,分配和释放是完全透明的,只是简单地使用或不使用寄存器。


一个更复杂的示例将在堆栈上执行相同的操作:
int foo(int);
void bar(int*);
void bar()

    
        int a[10];
        for (int i=0 ; i < 10 ; i++)
            a[i] = foo(i);
        bar(a);
    
    
        int b[10];
        for (int j=0 ; j < 10 ; j++)
            b[j] = foo(j);
        bar(b);
    

另外,咨询second godbolt.org example 产生:

        xor     ebx, ebx ; <--- this is simply part of the next block
        sub     rsp, 48;   <--- allocating the stack space
.L2:
        mov     edi, ebx
        call    foo(int)
        mov     DWORD PTR [rsp+rbx*4], eax
        add     rbx, 1
        cmp     rbx, 10
        jne     .L2
        mov     rdi, rsp
        xor     ebx, ebx ; <--- this is simply part of the next block
        call    bar(int*)
.L3:
        mov     edi, ebx
        call    foo(int)
        mov     DWORD PTR [rsp+rbx*4], eax
        add     rbx, 1
        cmp     rbx, 10
        jne     .L3
        mov     rdi, rsp
        call    bar(int*)
        add     rsp, 48  ; <-- deallocating the stack space

同样,这两种情况的代码是相同的。堆栈上没有释放或分配变量。行:

        a[i] = foo(i);

被翻译成

        mov     DWORD PTR [rsp+rbx*4], eax

它只是写相对于堆栈指针(rsp)的数据。它基本上是根据其相对于堆栈指针的位置找到a 的内容。两个代码块之间的堆栈指针没有更新,它只是通过将堆栈指针复制到rdi来传递给bar()

        mov     rdi, rsp
        call    bar(int*)


正如我所展示的,通常在块运行期间不会发生分配和解除分配。通常,在函数的开始和结束时,堆栈指针会更新以反映变量。

与简单的整数值不同,具有析构函数的类型更复杂,但我不会对此进行深入讨论,因为问题中没有提出。

【讨论】:

【参考方案4】:

如果你正在创建一个对象并分配一个指向它的指针

Animal* animal =new Animal() ;

它会在内存中分配一个内存区域。就像你正在盖房子并给某人地址。

delete animal;

会去那个内存区域并销毁它。就像你用地址追踪房子并销毁它一样。那么它所持有的价值将是垃圾。作为一个好的做法,您必须将房屋地址设为nullptr

animal = nullptr;

【讨论】:

【参考方案5】:

就像 user3344003 所说,变量 x 分配在堆栈上,即堆栈指针指向的位置。如果您不知道堆栈指针是什么,则需要阅读它。当函数结束时,对(所有)局部变量的引用将丢失,包括 x。现在用作堆栈的内存被重用,包括 x 在内的所有局部变量的值因此丢失,因为它们的引用(堆栈上的内存地址)丢失了,也因为内存被重用于其他变量或用途。

【讨论】:

对于当前的 C++ 实现,“堆栈指针”的概念在实践中存在,但标准并未强制要求或引用它。自动存储必须实现一个堆栈,一个 LIFO 结构,但标准并不要求该堆栈是机器堆栈。特别是,对于协程来说,使用链表堆栈是有意义的。【参考方案6】:

这种行为不是由 C++ 定义的,但我知道的每个实现都会做这样的事情。

编译器确定函数中的局部变量需要多少字节。编译器创建的 prolog 代码执行如下操作:

  SUB #NUMBEROFNEEDEDBYTES, SP

从堆栈分配存储。

在内部,编译器为每个变量分配一些偏移量。比方说:

 X=12

然后

 12(SP) 

成为X的地址。

 ADD #4, 12(SP)

等价于

X += 4 ;

在函数结束时,编译器会创建如下代码:

 ADD #NUMBEROFNEEDEDBYTES, SP

释放内存。

X 然后具有该位置的堆栈中发生的任何内容的初始值。您在 X 中输入的任何内容都将保留给下一个函数。

【讨论】:

【参考方案7】:

即使在 delete 调用之后,对象也不会真正从内存中删除。这是一个漏洞,敏感信息可以通过转储文件泄露。因此,最好将敏感对象替换为虚拟对象并将其删除。

【讨论】:

以上是关于C++ 在变量的内存地址上做了啥来“释放”它?的主要内容,如果未能解决你的问题,请参考以下文章

第 6 行实际上做了啥来检查排列是不是已经显示?

CALL -151 它在 APPLE 上做了啥][

grunt "test command" 在 npm init 上做了啥

你曾经工作过的最具挑战性的开发环境是啥?你做了啥来克服这些限制?

C++程序的内存分区,为什么要使用动态内存,动态内存的分配使用释放

在 C++ 中删除其类的实例后,分配给 cpp 中定义的全局静态变量的内存是不是被释放?