TL; DR :当我在OS X Yosemite下的Mac上运行我的C ++程序时,指针在函数返回时被破坏。我该如何阻止它发生? (为什么?)
在this sample program中,我有一个category_map<T>
类型的数据结构,实际上只是一个
map<string, list<pair<string, T> > >
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;
}
但是,当我在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 ++函数的一部分,我在汇编方面不够流畅,无法理解它在大规模上做了什么,{{3是垃圾,所以我不能把代码放在上下文中。 (我还在破坏指针的指令之前捕获了backtrace,如果由于某种原因有帮助的话。)
由于我仅使用指针类型实例化category_map<T>
,因此我可以将get
的返回类型更改为T
(而不是T&
)这似乎解决了(或至少解决)问题。但是如果数据结构可以容纳大对象并通过引用返回它们,那么它会使数据结构更有用。我认为这应该是可行的。另外,无论我在编码时犯了什么错误,我都想明白,所以我不再重复。任何人都可以指出我做错了什么,以及在不更改API的情况下修复它的正确方法吗?
答案 0 :(得分:5)
使用
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
返回T const &
1 。否则,您将遇到尝试从const
成员函数返回对非const成员的引用的问题;这会破坏const正确性,因此是禁止的(如果允许的话,你可以通过该引用更改常量对象)。
1 你也可以让get const()
返回一个值而不是一个引用,但似乎没有理由强制该副本。