代码之家  ›  专栏  ›  技术社区  ›  David Z

从函数返回时指针损坏

  •  1
  • David Z  · 技术社区  · 11 年前

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


    在里面 this sample program ,我的数据结构类型为 category_map<T> 这实际上只是一个

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

    这个 category_map 类有几个方法,包括 get(string& name) 它拉动 list 存储在给定的 name 并返回 T 从该列表的第一个元素开始。在我的情况下, T 是指针类型。代码从第一个 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 在下面的下一个代码列表中-已损坏。例如,它可能变成 0x3000100809c00 0x5000100809c00 损坏的指针始终是原始指针,在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
    

    ( more context )但这并没有多大帮助,因为它不是C/C++函数的一部分,我在汇编方面不够流利,无法理解它在大规模地做什么 backtrace 是垃圾,所以我不能将代码放到上下文中。(我还拍摄了 values of the registers 正好在损坏指针的指令之前,以防由于某种原因而有所帮助。)

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

    1 回复  |  直到 11 年前
        1
  •  5
  •   Wintermute    11 年前

    具有

    list_type the_list = map_iterator->second;
    

    你复制了 map_iterator->second . the_list 是函数局部对象。然后

    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 返回a T const & 1. 。否则,您将遇到尝试从 const 成员功能;这将破坏常量正确性,因此是禁止的(如果允许,您将能够通过该引用更改常量对象)。

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