C++在变量的内存地址上做什么来"deallocate"它?
What does C++ do at the memory address of a variable to "deallocate" it?
例如。
当一个具有局部整数变量 x 的函数结束时,C++对存储在对应于 x 的内存位置的值做什么?
它是否插入随机值?
可能正在发生的事情 - 什么都没有。删除某些内容需要资源,因此它只是调整指向此内存的堆栈的指针。这将导致下次使用此内存时覆盖它。
使用变量而不初始化变量时,您可以看到类似的事情。
int i;
i
将包含"垃圾"数据,则此"垃圾"来自内存中此特定位置的使用时间。可以是旧照片,文本文件或w/e。
这在很大程度上取决于实现。
在大多数情况下,什么都不做。您解除分配(这会更改堆栈指针)。在这种情况下,您不会修改内存。
在一些非常晦涩的情况下,你可能会发现做了不同的事情。在一个示例中,RTOS RTEMS可以设置为写入存储。它可能会写一些特定的内容,如0xCDCDCDCD
或0xDEADBEEF
.这在调试这些 RTOS 系统中的内存问题时非常有用,因为当您使用坏内存时很容易识别。但这是非常罕见的。
理论上没有指定,但实际上没有对单个基本类型对象(如 int、double、T* 等)进行分配。在某些情况下,堆栈指针不会被修改,编译器只是将空间重用为其他变量:
for (int i=0 ; i < 10 ; i++)
foo(i);
for (int j=0 ; j < 10 ; j++)
foo(j);
最有可能的是,编译器将重用i
的空间来分配j
。这很容易在 gcc godbolt.org 上验证:
.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
不仅循环相同,它们甚至不使用堆栈。在 'ebx 上分配i
和j
变量,而不是堆栈。在此示例中,分配和解除分配是完全透明的,并且是寄存器的简单使用或不使用。
一个更复杂的示例将在堆栈上执行相同的操作:
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);
}
}
此外,咨询第二个 godbolt.org 示例会产生:
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*)
正如我所展示的,通常在区块运行期间不会发生分配和交易。通常,在函数的开头和结尾,堆栈指针会更新以反映变量。
与简单的整数值不同,带有析构函数的类型更复杂,但我不会更深入地讨论这个问题,因为问题中没有提出这个问题。
如果要创建对象并为其分配指针
Animal* animal =new Animal() ;
它将在内存中分配一个内存区域。这就像你在盖房子,给某人地址。
delete animal;
将转到该内存区域并销毁它。就像你用地址追踪房子并摧毁它一样。那么它所持有的价值将是垃圾。作为一种好的做法,您必须将房屋的地址设为nullptr
。
animal = nullptr;
就像user3344003所说的那样,变量x被分配到堆栈上,即堆栈指针指向的位置。如果您不知道堆栈指针是什么,则需要阅读它。当函数结束时,对(所有)局部变量的引用将丢失,包括 x。现在,用作堆栈的内存被重用,因此包括 x 在内的所有局部变量的值都会丢失,因为它们的引用(堆栈上的内存地址)丢失了,也因为内存被重用于其他变量或目的。
这种行为不是由C++定义的,但是我知道的每个实现都做了这样的事情。
编译器确定函数中的局部变量需要多少字节。编译器创建执行如下操作的 prolog 代码:
SUB #NUMBEROFNEEDEDBYTES, SP
这将从堆栈中分配存储。
在内部,编译器为每个变量分配一些偏移量。比方说:
X=12
然后
12(SP)
成为 X 的地址。
ADD #4, 12(SP)
相当于
X += 4 ;
在函数结束时,编译器创建如下操作的 epilog 代码:
ADD #NUMBEROFNEEDEDBYTES, SP
以释放内存。
然后,X 具有该位置堆栈上碰巧的任何内容的初始值。无论你在 X 中输入什么,都会保留在下一个函数中。
即使在删除调用后,该对象也不会实际从内存中删除。这是一个漏洞,敏感信息可以通过转储文件泄露。因此,最佳做法始终是用虚拟对象替换敏感对象并将其删除。
- 为不同配置设置MSVC_RUNTIME_LIBRARY的正确方法是什么
- 警告处理为错误这里有什么问题
- 什么时候调用组成单元对象的析构函数
- #定义c-预处理器常量..我做错了什么
- 努力将整数转换为链表。不知道我在这里做错了什么
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 什么时候在C++中返回常量引用是个好主意
- 当在同一名称空间中有两个具有相同签名的函数时,会发生什么
- C++避免重复声明的语法是什么
- c++库的公共头文件中应该包含什么
- 问题:什么是QAbstractItemView::NoEditTriggers的反面
- 有什么方法可以遍历结构吗
- 当类在C++中定义时,有什么方法可以"register"类吗?
- ifstream什么都没读
- 在C++中,将大的无符号浮点数四舍五入为整数的最佳方法是什么
- 实现无开销push_back的最佳方法是什么
- C++从另一个类访问公共静态向量的正确方法是什么
- "throw expression code" 1e7 >返回 d 是什么?投掷标准::overflow_error( "too big" ) : d;意味 着?
- 我应该使用什么来代替void作为变体中的替代类型之一
- C++在变量的内存地址上做什么来"deallocate"它?