【发布时间】:2015-03-24 15:15:48
【问题描述】:
TL;DR:当我在 OS X Yosemite 下的 Mac 上运行 C++ 程序时,当函数返回时指针会损坏。我该如何阻止它发生? (为什么?)
在this sample program 中,我有一个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 中 - 已损坏。例如,它可能变为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>,我可以将get 的返回类型更改为T(而不是T&),这似乎可以解决(或至少解决)问题。但是,如果它可以容纳大对象并通过引用返回它们,它会使数据结构更普遍有用,我认为这应该是可能的。另外,无论我在编码时犯了什么错误,我都想理解,所以我不会再犯了。谁能指出我做错了什么,以及在不更改 API 的情况下修复它的正确方法吗?
【问题讨论】:
标签: c++ macos pointers memory c++98