从函数返回时指针已损坏

Dav*_*d Z 1 c++ memory macos pointers c++98

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


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

map<string, list<pair<string, T> > >
Run Code Online (Sandbox Code Playgroud)

category_map类有两个方法,包括get(string& name)其中拉动list存储在给定的name,并返回T从该列表中的第一个元素.就我而言,T是指针类型.代码从第一个pair中检索的指针list- 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;
}
Run Code Online (Sandbox Code Playgroud)

但是,当我在Mac(OS X Yosemite)上编译和运行代码时,而不是在Linux上,在从这个函数清理过程中的某个地方,某些内容写入内存中的相同位置,以便返回指针 - 存储在ip下面的下一个代码清单中的变量中 - 已损坏.例如,它可能变成0x3000100809c000x5000100809c00.损坏的指针始终是原始指针,在8字节地址的第2个最高有效字节中设置一个或几个额外位.

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;
}
Run Code Online (Sandbox Code Playgroud)

使用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
Run Code Online (Sandbox Code Playgroud)

(更多上下文),但这没有多大帮助,因为它不是C/C++函数的一部分,我在汇编时不够流畅,无法理解它在大规模上做了什么,而且回溯是垃圾,所以我不能把上下文中的代码.(我还在破坏指针的指令之前捕获了寄存器,以防由于某种原因.)

由于我category_map<T>只使用指针类型进行实例化,因此我可以将返回类型更改getT(而不是T&),并且确实可以解决(或至少解决)问题.但是如果它可以容纳大型对象并通过引用返回它,那么它使数据结构更加普遍有用,我认为这应该是可能的.另外,无论我在编码时犯了什么错误,我都想明白,所以我不再重复.任何人都可以指出我做错了什么,以及正确的方法来修复它而不更改API?

Win*_*ute 5

list_type the_list = map_iterator->second;
Run Code Online (Sandbox Code Playgroud)

你复制一份map_iterator->second.the_list是一个函数本地对象.然后

T& p = the_list.front().second;
return p;
Run Code Online (Sandbox Code Playgroud)

返回对与此函数本地对象一样长的东西的引用,并在函数离开时被销毁.参考悬挂.

它看起来好像你不打算复制列表,所以

//          +------ 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;
Run Code Online (Sandbox Code Playgroud)

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

1您也可以get const()返回值而不是引用,但似乎没有理由强制该副本.