从函数返回时指针损坏
Pointer corrupted while returning from a function
TL;DR:当我 运行 我的 C++ 程序在 OS X Yosemite 下的 Mac 上时,一个当函数正在 returning 时指针被损坏。我该如何阻止它发生? (为什么?)
在 this sample program 中,我有一个 category_map<T>
类型的数据结构,它实际上只是一个
map<string, list<pair<string, T> > >
category_map
class 有几个方法,包括 get(string& name)
提取存储在给定 name
和 [=61= 下的 list
]s 来自该列表第一个元素的 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,某处从这个函数清理的过程中,某些东西写入内存中的相同位置,因此 returned 指针 - 存储在下面下一个代码清单中的变量 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 [=13=]x2,%eax
0x00007fff9318827c: jb 0x7fff9318828d
0x00007fff9318827e: shl [=13=]x4,%rax
=> 0x00007fff93188282: mov %r10w,-0x2(%rax,%rdx,1)
0x00007fff93188288: mov %r10w,0x10(%rdx)
0x00007fff9318828d: test %r10w,%r10w
0x00007fff93188291: jne 0x7fff93188299
(more context) but this is not much help because it's not part of a C/C++ function, I'm not fluent enough in assembly to understand what it's doing on a large scale, and the backtrace is garbage so I can't put the code in context. (I've also captured the values of the registers 就在破坏指针的指令之前,以防出于某种原因有帮助。)
由于我仅使用指针类型实例化 category_map<T>
,因此我 可以 将 get
的 return 类型更改为 T
(而不是 T&
),这似乎可以解决(或至少解决)问题。但如果它可以容纳大对象并通过引用 return 它们,它会使数据结构更普遍有用,我认为这应该是可能的。另外,无论我在编码时犯了什么错误,我都想理解,所以我不会再犯了。任何人都可以指出我做错了什么,以及在不更改 API 的情况下修复它的正确方法吗?
有
list_type the_list = map_iterator->second;
您复制了 map_iterator->second
。 the_list
是函数局部对象。那么
T& p = the_list.front().second;
return p;
returns 对与此函数局部对象一样长的东西的引用,并在函数离开时被销毁。参考悬挂。
在我看来,您似乎并不打算复制该列表,所以
// +------ 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
return 成为 T const &
1。否则,您会遇到从 const
成员函数中尝试 return 对非常量成员的引用的问题;这会破坏常量正确性,因此被禁止(如果允许,您将能够通过该引用更改常量对象)。
1 你也可以让 get const()
return 成为一个值而不是一个引用,但似乎没有理由强制这样做复制.
TL;DR:当我 运行 我的 C++ 程序在 OS X Yosemite 下的 Mac 上时,一个当函数正在 returning 时指针被损坏。我该如何阻止它发生? (为什么?)
在 this sample program 中,我有一个 category_map<T>
类型的数据结构,它实际上只是一个
map<string, list<pair<string, T> > >
category_map
class 有几个方法,包括 get(string& name)
提取存储在给定 name
和 [=61= 下的 list
]s 来自该列表第一个元素的 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,某处从这个函数清理的过程中,某些东西写入内存中的相同位置,因此 returned 指针 - 存储在下面下一个代码清单中的变量 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 [=13=]x2,%eax
0x00007fff9318827c: jb 0x7fff9318828d
0x00007fff9318827e: shl [=13=]x4,%rax
=> 0x00007fff93188282: mov %r10w,-0x2(%rax,%rdx,1)
0x00007fff93188288: mov %r10w,0x10(%rdx)
0x00007fff9318828d: test %r10w,%r10w
0x00007fff93188291: jne 0x7fff93188299
(more context) but this is not much help because it's not part of a C/C++ function, I'm not fluent enough in assembly to understand what it's doing on a large scale, and the backtrace is garbage so I can't put the code in context. (I've also captured the values of the registers 就在破坏指针的指令之前,以防出于某种原因有帮助。)
由于我仅使用指针类型实例化 category_map<T>
,因此我 可以 将 get
的 return 类型更改为 T
(而不是 T&
),这似乎可以解决(或至少解决)问题。但如果它可以容纳大对象并通过引用 return 它们,它会使数据结构更普遍有用,我认为这应该是可能的。另外,无论我在编码时犯了什么错误,我都想理解,所以我不会再犯了。任何人都可以指出我做错了什么,以及在不更改 API 的情况下修复它的正确方法吗?
有
list_type the_list = map_iterator->second;
您复制了 map_iterator->second
。 the_list
是函数局部对象。那么
T& p = the_list.front().second;
return p;
returns 对与此函数局部对象一样长的东西的引用,并在函数离开时被销毁。参考悬挂。
在我看来,您似乎并不打算复制该列表,所以
// +------ 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
return 成为 T const &
1。否则,您会遇到从 const
成员函数中尝试 return 对非常量成员的引用的问题;这会破坏常量正确性,因此被禁止(如果允许,您将能够通过该引用更改常量对象)。
1 你也可以让 get const()
return 成为一个值而不是一个引用,但似乎没有理由强制这样做复制.