意外的运行时错误(分段错误)
Unexpected runtime error (segmentation fault)
我目前正在优化内存密集型应用程序以占用更少的内存。我在下面的代码中试图做的是动态分配文件流对象 ifstream
和 ofstream
以便在不再需要使用它们之后准确地释放它们。该代码对于 ofstream
的 allocation/de-allocation 完美运行,但由于 ifstream
的内存内容被取消分配时可能出现分段错误,因此在运行时崩溃。以下为原代码片段:
#include <fstream>
using namespace std;
// Dummy class to emulate the issue at hand
class dummy {
private:
int randINT;
static bool isSeeded;
public:
dummy() { randINT=rand(); }
int getVal() { return randINT; }
};
bool dummy::isSeeded=false;
int main(int argc, const char* argv[]) {
// Binary file I/O starts here
dummy * obj;
ofstream * outputFile;
ifstream * inputFile;
outputFile=new ofstream("bFile.bin",ios::binary);
if (!(*outputFile).fail()) {
obj=new dummy;
cout << "Value to be stored: " << (*obj).getVal() << "\n";
(*outputFile).write((char *) obj, sizeof(*obj)); // Save object to file
(*outputFile).close();
delete obj;
// don't assign NULL to obj; obj MUST retain the address of the previous object it pointed to
} else {
cout << "Error in opening bFile.bin for writing data.\n";
exit(1);
}
delete outputFile; // This line throws no errors!
inputFile=new ifstream("bFile.bin",ios::binary);
if (!(*inputFile).fail()) {
(*inputFile).read((char *) obj,sizeof(dummy)); // Read the object of type 'dummy' from the binary file and allocate the object at the address pointed by 'obj' i.e. the address of the previously de-allocated object of type 'dummy'
cout << "Stored Value: " << (*obj).getVal() << "\n";
(*inputFile).close();
} else {
cout << "Error in opening bFile.bin for reading data.\n";
exit(1);
}
delete inputFile; // Runtime error is thrown here for no reason!
cout << "\n-----END OF PROGRAM-----\n";
}
当用户调用保存函数时,上面的代码将一个对象保存到一个二进制文件中。如上面的代码所述,inputFile
指针的取消分配会引发运行时错误。
请注意,我正在使用 clang(更具体地说,clang++)来编译项目。
提前致谢。
std::basic_istream::read(char_type*address, streamsize count)
最多读取 count
个字符并将它们放在 address
处。它 不会 在 address
处创建任何对象,正如您似乎相信的那样。 address
必须指向大小至少为 count*sizeof(char_type)
的有效内存块。通过传递 delete
d 对象的地址,您违反了该条件:您的代码已损坏并且分段错误 并不意外 。
编辑
你正在做的是undefined behaviour, short UB. When you do that, nothing is guaranteed and any logic about what happens in which order invalid. The program is allowed to do anything, including immediately crashing, running for a while and then crashing, or "make demons fly out of your nose"。
在你的情况下,我怀疑 std::basic_istream::read()
写入不受保护的内存会导致某些数据被覆盖,例如另一个对象的地址,这会导致分段错误。但这纯粹是猜测,并不值得追求。
在你的例子中没有创建对象。二进制文件只包含一些字节。 read()
将它们复制到提供的地址,该地址不是为此目的保留的。要避免 UB,只需添加
obj = new dummy;
在read
之前创建对象。
如果你想重新使用前一个对象的内存,你可以使用placement new(见link的第9点和第10点)。例如
char*buffer = nullptr; // pointer to potential memory buffer
if(writing) {
if(!buffer)
buffer = new char[sizeof(dummy)]; // reserve memory buffer
auto pobj = new(buffer) dummy(args); // create object in buffer
write(buffer,sizeof(dummy)); // write bytes of object
pobj->~pobj(); // destruct object, but don't free buffer
}
if(reading) {
if(!buffer)
buffer = new char[sizeof(dummy)]; // not required if writing earlier
read(buffer,sizeof(dummy)); // read bytes into object
auto pobj = reinterpret_case<dummy*>(buffer); // no guarantees here
use(pobj); // do something with the object read
pobj->~pobj(); // destruct object
}
delete[] buffer; // free reserved memory
请注意,如果读取未生成有效对象,则该对象的后续使用(即对其析构函数的调用)可能会崩溃。
然而,所有这些微优化无论如何都是毫无意义的(只有当你可以避免 many 调用 new
和 delete
时才值得这样做)。不要浪费你的时间。
我目前正在优化内存密集型应用程序以占用更少的内存。我在下面的代码中试图做的是动态分配文件流对象 ifstream
和 ofstream
以便在不再需要使用它们之后准确地释放它们。该代码对于 ofstream
的 allocation/de-allocation 完美运行,但由于 ifstream
的内存内容被取消分配时可能出现分段错误,因此在运行时崩溃。以下为原代码片段:
#include <fstream>
using namespace std;
// Dummy class to emulate the issue at hand
class dummy {
private:
int randINT;
static bool isSeeded;
public:
dummy() { randINT=rand(); }
int getVal() { return randINT; }
};
bool dummy::isSeeded=false;
int main(int argc, const char* argv[]) {
// Binary file I/O starts here
dummy * obj;
ofstream * outputFile;
ifstream * inputFile;
outputFile=new ofstream("bFile.bin",ios::binary);
if (!(*outputFile).fail()) {
obj=new dummy;
cout << "Value to be stored: " << (*obj).getVal() << "\n";
(*outputFile).write((char *) obj, sizeof(*obj)); // Save object to file
(*outputFile).close();
delete obj;
// don't assign NULL to obj; obj MUST retain the address of the previous object it pointed to
} else {
cout << "Error in opening bFile.bin for writing data.\n";
exit(1);
}
delete outputFile; // This line throws no errors!
inputFile=new ifstream("bFile.bin",ios::binary);
if (!(*inputFile).fail()) {
(*inputFile).read((char *) obj,sizeof(dummy)); // Read the object of type 'dummy' from the binary file and allocate the object at the address pointed by 'obj' i.e. the address of the previously de-allocated object of type 'dummy'
cout << "Stored Value: " << (*obj).getVal() << "\n";
(*inputFile).close();
} else {
cout << "Error in opening bFile.bin for reading data.\n";
exit(1);
}
delete inputFile; // Runtime error is thrown here for no reason!
cout << "\n-----END OF PROGRAM-----\n";
}
当用户调用保存函数时,上面的代码将一个对象保存到一个二进制文件中。如上面的代码所述,inputFile
指针的取消分配会引发运行时错误。
请注意,我正在使用 clang(更具体地说,clang++)来编译项目。
提前致谢。
std::basic_istream::read(char_type*address, streamsize count)
最多读取 count
个字符并将它们放在 address
处。它 不会 在 address
处创建任何对象,正如您似乎相信的那样。 address
必须指向大小至少为 count*sizeof(char_type)
的有效内存块。通过传递 delete
d 对象的地址,您违反了该条件:您的代码已损坏并且分段错误 并不意外 。
编辑
你正在做的是undefined behaviour, short UB. When you do that, nothing is guaranteed and any logic about what happens in which order invalid. The program is allowed to do anything, including immediately crashing, running for a while and then crashing, or "make demons fly out of your nose"。
在你的情况下,我怀疑 std::basic_istream::read()
写入不受保护的内存会导致某些数据被覆盖,例如另一个对象的地址,这会导致分段错误。但这纯粹是猜测,并不值得追求。
在你的例子中没有创建对象。二进制文件只包含一些字节。 read()
将它们复制到提供的地址,该地址不是为此目的保留的。要避免 UB,只需添加
obj = new dummy;
在read
之前创建对象。
如果你想重新使用前一个对象的内存,你可以使用placement new(见link的第9点和第10点)。例如
char*buffer = nullptr; // pointer to potential memory buffer
if(writing) {
if(!buffer)
buffer = new char[sizeof(dummy)]; // reserve memory buffer
auto pobj = new(buffer) dummy(args); // create object in buffer
write(buffer,sizeof(dummy)); // write bytes of object
pobj->~pobj(); // destruct object, but don't free buffer
}
if(reading) {
if(!buffer)
buffer = new char[sizeof(dummy)]; // not required if writing earlier
read(buffer,sizeof(dummy)); // read bytes into object
auto pobj = reinterpret_case<dummy*>(buffer); // no guarantees here
use(pobj); // do something with the object read
pobj->~pobj(); // destruct object
}
delete[] buffer; // free reserved memory
请注意,如果读取未生成有效对象,则该对象的后续使用(即对其析构函数的调用)可能会崩溃。
然而,所有这些微优化无论如何都是毫无意义的(只有当你可以避免 many 调用 new
和 delete
时才值得这样做)。不要浪费你的时间。