在学习 C# 时,我发现重新实现 List 或 LinkedList 之类的东西很有趣,只是为了了解它的工作原理以及实现它时可能遇到的潜在问题。
在学习 C++ 时,由于我有一些 C# 经验,我决定挑战自己,尝试实现比章节末尾事件要求的更高级的代码。因此,我最终尝试在 C++ 中实现一个非通用列表来尝试一下,但最终收到了一个非常奇怪的段错误。
关于代码的一个小免责声明,在尝试修复它时,我最终重构了它并删除了一些东西(尽管它们都没有改变错误),所以一两个函数没有用,但在几个小时后尝试理解问题,我不想删除或更改任何内容并意外地解决问题。无论如何,这是代码。
class List {
private:
int *ListData;
size_t ListSize;
size_t Pos;
std::stack<size_t> NullList;
size_t InternalNull();
inline size_t PosOnly();
void Check();
size_t (List::*NextNumber)();
public:
List(bool InternalNullHandle);
List(size_t DefaultSize, bool InternalNullHandle);
~List();
void Add(const int SalesRef);
int GetCopy(size_t Pos);
int Get();
};
List::List(bool InternalNullHandle) {
NextNumber = (InternalNullHandle) ? &List::InternalNull : &List::PosOnly;
ListSize = 32;
ListData = new int[32];
Pos = 0;
}
List::List(size_t DefaultSize, bool InternalNullHandle) {
NextNumber = (InternalNullHandle) ? &List::InternalNull : &List::PosOnly;
ListSize = DefaultSize;
ListData = new int[DefaultSize];
Pos = 0;
}
List::~List() {
delete[] ListData;
}
void List::Check() {
if (Pos >= ListSize) {
size_t OldSize = ListSize;
ListSize*=2;
int *Buffer = new int[ListSize];
memcpy(Buffer, ListData, sizeof(int)*OldSize);
if (ListData != NULL) {
delete[] ListData; //POINT OF INTEREST ONE
ListData = NULL;
}
else {
std::cerr<<"ListData is null."<<std::endl;
}
ListData = Buffer;
}
}
size_t List::InternalNull() {
if (NullList.size() != 0) {
size_t ToReturn = NullList.top();
NullList.pop();
return ToReturn;
}
return PosOnly();
}
inline size_t List::PosOnly() {
size_t Old = Pos;
++Pos;
Check();
return Old;
}
inline void List::Add(const int SalesRef) {
//size_t Value = (this->*NextNumber) ();
//ListData[Value] = SalesRef;
//if the above code is utilised instead, everything works fine
ListData[ (this->*NextNumber) () ] = SalesRef; //POINT OF INTEREST TWO
}
inline int List::GetCopy(size_t Pos) {
return ListData[Pos];
}
我通常不发帖,但会广泛搜索。我最终安装并运行了 valgrind,当使用兴趣点二时,它在兴趣点一处出现读取和写入错误。然而,当注释掉兴趣点二并使用注释行时,没有出现任何问题。
问题仅在 128 次迭代后出现,这意味着它正确加倍到 64 和 128,并且正确删除了数组。
此外,请注意,代码在使用 g++ 编译的 Windows 上运行得非常好。
我尝试使用单独的类重现该错误,但它工作得很好。
再说一遍,我知道我应该使用标准容器(而且我会的),但我喜欢了解一切,而我无法弄清楚这一点的事实非常烦人。再加上我什至无法重现它并且不得不复制这个不完整且设计糟糕的代码,只会让情况变得更糟。感谢您提前的帮助!
小幅编辑,如果真的很难阅读,我会添加注释并尝试清理代码而不破坏(好吧,更确切地说是修复)它。测试的操作系统是带有 mingw 的 Windows 7(可以运行)和带有 g++ 的 Debian(仅适用于未注释的注释行)。
最佳答案
语句有问题
ListData[ (this->*NextNumber) () ] = SalesRef; //POINT OF INTEREST TWO
是在获取ListData
字段的值和调用NextNumber
指向的成员函数之间没有顺序点。因此,编译器非常乐意在函数调用之前进行加载,然后在调用之后对其进行索引。但是,该调用可能会导致重新分配 ListData,因此调用之前获得的指针现在悬空(它指向刚刚删除的数组),并且会发生不好的事情。
使用注释掉的代码,可以强制函数调用在获取 ListData
之前发生,这样在调整大小重新分配后,获取将始终获得正确的值。
关于c++ - 函数指针返回值时出现奇怪的段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6700666/