从函数返回时指针已损坏

Pointer corrupted while returning from a function

本文关键字:指针 已损坏 返回 函数      更新时间:2023-10-16

TL;DR:当我在OS X Yosemite下的Mac上运行C++程序时,当函数返回时,指针会损坏。我该如何阻止它的发生?(为什么?)


在这个示例程序中,我有一个类型为category_map<T>的数据结构,它实际上只是一个

map<string, list<pair<string, T> > >

category_map类有几个方法,包括get(string& name),它提取存储在给定name下的list,并从该列表的第一个元素返回T。在我的例子中,T是一个指针类型。代码从list中的第一个pair检索到的指针(在下面的代码列表中应该是p)是有效的。调试器会话显示,函数最后一行的p的值(在析构函数运行之前的大括号)是一个有效的内存位置,比如0x100809c00

T& get(const string& name) const {
    cerr << "searching for " << name << endl;
    typename super::const_iterator map_iterator = super::find(name);
    // the real code doesn't assume it will be found
    list_type the_list = map_iterator->second;
    T& p = the_list.front().second;
    cerr << "found " << val_loc_string<T>(p) << endl;
    return p;
}

然而,当我在Mac(OS X Yosemite)上编译和运行代码时,而不是在Linux上,在清理该函数的过程中,有东西会写入内存中的同一位置,因此返回的指针(存储在下面列出的下一个代码中的变量ip中)已损坏。例如,它可能变成0x3000100809c000x5000100809c00。损坏的指针始终是原始指针,在8字节地址的第二个最高有效字节中设置了一个或几个额外的位。

int main(const int argc, const char** argv) {
    category_map<int*> imap;
    int a;
    imap.add("Q1", "m", &a);
    imap.add("Q1", "r", &a);
    imap.add("Q2", "m", &a);
    int* ip = imap.get("Q1");
    cerr << "return value: " << val_loc_string<int*>(ip) << endl;
    cout << *ip << endl;
}

使用GDB(通过MacPorts安装),我已经确定了将额外位写入内存位置的特定指令。

   0x00007fff93188279:  cmp    $0x2,%eax
   0x00007fff9318827c:  jb     0x7fff9318828d
   0x00007fff9318827e:  shl    $0x4,%rax
=> 0x00007fff93188282:  mov    %r10w,-0x2(%rax,%rdx,1)
   0x00007fff93188288:  mov    %r10w,0x10(%rdx)
   0x00007fff9318828d:  test   %r10w,%r10w
   0x00007fff93188291:  jne    0x7fff93188299

(更多上下文),但这并没有多大帮助,因为它不是C/C++函数的一部分,我对汇编不够流利,无法理解它在大规模执行什么,而且回溯是垃圾,所以我无法将代码放在上下文中。(我还捕获了在损坏指针的指令之前的寄存器值,以防出于某种原因有帮助。)

由于我只使用指针类型实例化category_map<T>,因此我可以get的返回类型更改为T(而不是T&),这似乎确实解决了(或至少解决了)问题。但是,如果数据结构可以容纳大型对象并通过引用返回它们,那么它会使数据结构更普遍地有用,我认为这应该是可能的。另外,无论我在编码过程中犯了什么错误,我都想理解,这样我就不会再犯了。有人能指出我做错了什么,以及在不更改API的情况下修复它的正确方法吗?

使用

list_type the_list = map_iterator->second;

您制作了一份map_iterator->second。CCD_ 21是一个函数局部对象。然后

T& p = the_list.front().second;
return p;

返回一个引用,该引用与此函数本地对象的寿命一样长,并且在函数保留时被销毁。该引用挂起。

在我看来,你似乎不打算复制这份名单,所以

//          +------ const because get() is const-qualified
//          v   v-- reference
list_type const &the_list = map_iterator->second;
//  v-- const because the_list is const
T const& p = the_list.front().second;

如果可以使get() const返回一个T const &1,则应该修复它。否则,您会遇到试图从const成员函数返回对非常数成员的引用的问题;这将破坏const的正确性,因此是被禁止的(如果允许,则可以通过该引用更改常量对象)。

1您也可以让get const()返回一个值而不是一个引用,但似乎没有理由强制复制。