gpt4 book ai didi

c++ - 使用就地新运算符堆栈展开破坏内存

转载 作者:行者123 更新时间:2023-12-01 23:52:34 25 4
gpt4 key购买 nike

我有一个非常讨厌的错误,已经困扰了一段时间了。情况是这样的,我正在创建一个内存文件系统。我为每个文件预先分配了数据 block ,以便在其中进行读取和写入。为了实现目录,我有一个简单的 std::vector 对象,其中包含目录中的所有文件。该 vector 对象位于每个目录中文件的顶部。因此,为了从目录中读取和写入,我将前 16 个字节读入字符缓冲区并将其类型转换为 vector (16 个字节,因为 sizeof(vector<T>) 在我的系统上是 16)。具体来说,前 16 个字节不是 vector 的元素,而是 vector 本身。然而,在我退出关键函数后, vector 会以某种方式被破坏。

以下代码不会引发异常,并且可以正确地将 vector 保存到字符缓冲区以供以后检索。

#include <vector>
char dblock[16];

typedef std::vector<int> Entries;
void foo() {
char buf[sizeof(Entries)];
Entries* test = new (buf)Entries();
test->push_back(0);
for (int i = 0; i < sizeof(std::vector<int>); ++i) {
dblock[i] = buf[i];
}
}

void bar() {
char buf[sizeof(Entries)];
for (int i = 0; i < sizeof(std::vector<int>); ++i) {
buf[i] = dblock[i];
}
Entries* test = (Entries*)buf;
test->back();
}

int main()
{
foo();
bar();
return 0;
}

但是,一旦将函数 foo 更改为包含这样的参数,每当我尝试使用迭代器时都会引发异常。

void foo(int this_arg_breaks_everything) {
char buf[sizeof(Entries)];
Entries* test = new (buf)Entries();
test->push_back(0);
for (int i = 0; i < sizeof(std::vector<int>); ++i) {
dblock[i] = buf[i];
}
}

查看反汇编结果,当函数拆除其帧堆栈时,我发现了问题汇编器:

add         esp,128h  <----- After stack is reduced, vector goes to an unusable state.
cmp ebp,esp
call __RTC_CheckEsp (0D912BCh)
mov esp,ebp
pop ebp
ret

我通过使用调试器测试 ((Entries*)dblock)->back() 是否返回异常来找到此代码。具体来说,异常(exception)是“访问冲突读取位置 0XCCCCCCCC”。有问题的位置是 std::vector 的 std::_Container_base12::_Myproxy->_Mycont->_Myproxy == 0XCCCCCCCC;你可以在 xutility 的第 165 行找到它。

是的,该错误仅在使用就地新运算符时才会发生。使用普通的 new 运算符然后将测试写入 dblock 不会引发异常。

因此,我得出的结论是,造成这种情况的最终原因是编译器在某种程度上进行了错误的堆栈展开,破坏了不应该出现的内存的某些部分。

编辑:为了清楚起见,更改了措辞。Edit2:解释魔数(Magic Number)。

最佳答案

在 Visual Studio 2013 中,这会产生错误,在查看 vector 的内部数据后,很容易找出原因。 Vector 分配一个内部对象来完成很多繁重的工作,这个内部对象又存储一个指向 vector 的指针,从而知道 vector 应该所在的位置。当 vector 的内存从一个位置移动到另一个位置时,它占用的内存会发生变化,因此该内部对象现在指向回内存,稍后会被调试代码删除。

看看你的代码,这看起来完全一样。 std::_Container_base12 是 vector 使用的基类,它有一个名为 myProxy 的成员。 myProxy 是一个负责繁重工作的内部对象,并且有一个指向包含它的 vector 的指针。当您移动 vector 时,您无法更新此指针,因此当您要使用移动的 vector 数据时,它会尝试使用 myProxy,它仍在尝试引用回被删除的原始 vector 位置。由于该数据区域已被删除,因此它会在其中查找指针,并找到“CCCCCCCC”,这就是调试代码对已删除的内存数据所做的操作。它尝试访问该内存位置,然后一切都崩溃了。

关于c++ - 使用就地新运算符堆栈展开破坏内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26623782/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com