SIGABRT in binary read/write - c++

I wrote a very small code snippet and have already gotten the following error:
malloc: *** error for object 0x100100080: pointer being freed was not allocated
Problem is, I have no idea what pointer the compiler's talking about. I pass a variable in by address to the read/write functions, but I never freed it as far as I know. Where's the error in my code? I ran it with Leaks and Zombies, but got nothing.
Here's my program:
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
class Bank
{
private:
string __name;
public:
Bank()
{
__name = "";
}
Bank(string name)
{
__name = name;
}
string getName() const { return __name; }
};
int main (int argc, char * const argv[])
{
Bank bank("Bank of America");
Bank bank2;
cout << "Bank1: " << bank.getName() << endl;
string filename = bank.getName() + ".bank";
ofstream fout(filename.c_str(), ios::binary);
if (fout.good())
fout.write((char *)&bank, sizeof(bank));
fout.close();
ifstream fin(filename.c_str(), ios::binary);
if (fin.good())
fin.read((char *)&bank2, sizeof(bank2));
fin.close();
cout << "Bank2: " << bank2.getName() << endl;
return 0;
}

You can't read an object that contains a std::string (or anything that's not Plain Ol' Data) with fin.read()--
The object is read and written as a stream of bytes, but std:string contains a pointer to memory that is stored elsewhere and is not written with your fout.write() and is not initialized properly with your fin.read()
It is because it is not initialized properly with your fin.read() that you are getting the heap error; when the object goes out of scope, the destructor of the improperly initialized std::string is being called, and trying to free memory that it doesn't own.
You probably want to write a custom i/o method for your object and save or load it piece-by-piece. For a shortcut to doing this, use the Boost serialization library.

Because your Bank class contains a std::string, you can't read/write it as binary like you are thinking. A std::string has internal pointers. If you write it as binary, you are just going to be writing pointers and not the actual string contents. Likewise, when you read the string, you are going to be reading a pointer. In this case, you end up making both your bank and bank2 objects have strings which point to the same memory, so when that memory is freed it gets freed twice.
You'll need to have some other way of writing your bank data to a file. In this case, a simple ASCII file with the bank name would be fine.

You cannot do what you are doing, simply because std::string cannot be copied like that. Internally a string object allocates memory and a simple copy of the outer structure doesn't do what you expect.
You need to serialize this structure properly.

Don't use underscores, please
Pass objects by reference: Bank(string& name), please
This is evil: fout.write((char *)&bank, sizeof(bank));
You may want to write << and >> ostream operators of your Bank class.
For example:
friend std::ostream& operator<<(std::ostream &out, const Bank& b);
friend std::istream& operator>>(std::istream &out, const Bank& b);

Members functions write of ostream and read of istream are specifically designed to input and output binary data. If you do want to manipulate binary data, use the following:
ifstream fin(filename.c_str(), ios::in|ios::binary|ios::ate);
size = fin.tellg();
memblock = new char [size];
fin.seekg(0, ios::beg);
if (fin.good()){
fin.read(memblock, size);
fin.close();
}
delete[] memblock;

Related

Reading/Writing to a file in c++

I am trying to reading and write objects to a file in C++, writing the object works fine, reading gives segmentation core dump. I have commented the code for writing objects to file, while writing we can uncomment that part and comment the reading part.
#include<iostream>
#include<fstream>
#include<string>
using namespace std;
class RelianceMart{
string name;
double trolley_number;
public:
RelianceMart(){
name = "NA";
trolley_number = 0;
}
RelianceMart(string name, double trolley_number){
this->name = name;
this->trolley_number = trolley_number;
}
void setname(string name){
this->name = name;
}
string getname(){
return name;
}
void settrolleynumber(double trolley_number){
this->trolley_number = trolley_number;
}
double gettrolleynumber(){
return trolley_number;
}
};
int main(){
string name;
double trl_num;
RelianceMart mart[3];
RelianceMart obj;
// ofstream fout("PersistentStorage.txt");
/*
for(int i=0;i<3;i++){
cin>>name;
cin>>trl_num;
mart[i] = RelianceMart(name, trl_num);
fout.write((char *) & mart[i], sizeof(mart[i]));
}
fout.close();
*/
ifstream fin("PersistentStorage.txt");
while(!fin.eof()){
fin.read((char *) & obj,sizeof(obj));
cout<< obj.getname();
}
fin.close();
return 0;
}
The members of std::string is really nothing more than a member variable for the length, and a member variable being a pointer to the actual string contents.
Pointers are private and unique to a specific process in all modern protected multi-tasking operating systems, no other process (not even one started from the same program) can reuse the same pointer.
When you write the RelianceMart objects, you write the pointer of the name string object to the file. As mentioned above no other process can use this pointer, and therefore can't read the file.
Furthermore when you attempt to read the raw objects, you read raw data overwriting the existing data in the constructed object, and the object won't be properly constructed anymore.
You also don't open the file in binary mode, which is wrong since you write and read raw binary data, not text.
The common solution is to use serialization, and the most common way to do it is simply to overload the "output" and "input" operators << and >>.
In the overloaded functions you simply write and read each object as text, again using the formatted << and >> operators.
Lastly, please read Why is iostream::eof inside a loop condition considered wrong?
I would use a serialization framework, you could use Google's Protocol Buffers(https://developers.google.com/protocol-buffers/).
If you consider a fullblown framework overkill, you can always write your own serialization framework, I've done that, I did use the JSON-format to encode the object.

How do I read from an input file after passing the ifstream object to a function?

as the title suggests, I am having a problem with not being able to read from an input file after passing the ifstream object to a class function. Basically I'm trying to sort a list of numbers using a heap ADT implemented with an array.
int main() {
ifstream infile("input.txt");
HeapSort* heap = new HeapSort(20); // creates a heap (array) with size 20
heap->buildHeap(&infile);
return 0;
}
void HeapSort::buildHeap(ifstream* infile) {
int data;
while (infile >> data) {cout << data << endl;}
infile->close();
}
the error occurs in the conditional of the while loop inside buildHeap. The compiler can't recognize the operator ">>" between an 'int' and an 'ifstream' object. However, strangely enough, if I write that same while loop inside main(), it'll work just fine. Also of note is that if I remove the while loop, the compiler returns no errors. Meaning, simply the act of passing the ifstream object from main to buildHeap is OK.
Please avoid suggesting alternative ways of achieving this. I was asked to not use any special fstream functions like eof(). I can only use the ">>" operator to read from the desired file.
You're passing a pointer to a stream, so you need to dereference it:
while (*infile >> data)
If you want your code to look like what you say you did in main, then you pass a reference:
heap->buildHeap(infile);
//...
void HeapSort::buildHeap(ifstream& infile)
{
int data;
while (infile >> data) { ... }
infile.close();
}

Why does this cpp program fail?

Note: While I was debugging, I found that until the last line, the program run normally, but when going by the last bracket, a mistake window would pop up. I'm not quite familiar with C++ so I couldn't locate the problem. Please help!
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
class test {
public:
int x;
void add_list(int);
void display();
private:
vector<int> list;
};
void test::add_list(int op)
{
list.push_back(op);
}
void test::display()
{
cout << x << endl;
for (unsigned int i=0;i<list.size(); i++)
cout << "->" << list[i];
cout << endl;
}
int main (void)
{
test test1;
test1.x = 3;
test1.add_list(2);
test1.add_list(4);
int size = sizeof (test1);
ofstream fout ("data.dat", ios_base::binary);
fout.write((char *)&test1, size);
fout.close();
ifstream fin ("data.dat", ios_base::binary);
test test2;
fin.read((char *)&test2, size);
test2.display();
fin.close();
return 0;
}
These lines
fout.write((char *)&test1, size);
and
fin.read((char *)&test2, size);
won't work because the test class contains objects that contain pointers. std::list will allocated extra memory using new to store items that are pushed on to it. It will then keep pointers to those items. When you write the object to disk, it will still contain those pointers to memory. When you load the object back again, the pointers will contain the same value, but your program may not have the same memory allocated and certainly won't have it allocated for the object.
In your case test2 appears to work because its internal pointers end up being the same as test1, however when you program finishes, the test1 destructor releases the memory that it allocated, then the test2 destructor tries to release the same memory, leading to your error.
To fix it, you should change your code to write the object in a defined format that doesn't use pointers (e.g. write out the item count followed by each items integer value). Then read them in the same way. One simple fwrite won't be able to do it.

C++: Problems reading input from a binary file

I have a class AccountManagement in AccountManagement.cpp. I have another class called Account in Account.cpp. I have a template that Orders the given data inside the list using OrdereList class, which also has it's own iterator. The AccountManagement class outputs the Accounts list in a binary file as shown below:
void AccountManagement::saveData(const char * file) //saves data in the specified binary file
{
ofstream out(file, ios::out | ios::binary);
if(!out)
{
cerr<<"Problem opening output file!"<<endl;
}
OrderedList<Account>::iterator it = this->account_manager.begin();
for(int i = 0; i < this->total_accounts; i++)
{
Account temp = *it;
out.write((char*)&temp, sizeof(Account));
it++;
}
out.close();
}
I have defined a following function inside AccountManagement class that reads all the data from binary file and outputs it. This function works perfectly fine. It is shown here:
void AccountManagement::output()
{
ifstream in("accounts.dat", ios::in | ios::binary);
if(!in)
{
cerr<<"File doesn't exist!"<<endl;
exit(1);
}
Account acc;
while(in.read((char*)&acc, sizeof(Account)))
{
cout<<acc;
}
in.close();
}
However, when I use this same function (with different name) in another file, which has Account.h header file as well to retrieve data from the same "account.dat" file it gives me segmentation fault. What could be the problem? Following is the function:
void loadData()
{
ifstream in("accounts.dat", ios::in | ios::binary);
if(!in)
{
cerr<<"File doesn't exist!"<<endl;
exit(1);
}
Account acc;
while(in.read((char*)&acc, sizeof(Account)))
{
cout<<acc;
}
in.close();
}
Account's class declaration:
class Account
{
friend ostream& operator<<(ostream&,const Account&); //overloading << operator
friend istream& operator>>(istream&,Account&); //overloading >> operator
public:
void operator=(const Account&); //overloading = operator
bool operator<=(const Account&); //overloading <= operator
bool operator<(const Account&); //overloading < operator
private:
string number; //Account Number
char name[100]; //Account holder's name
char sex; //M or F indicating the gender of account holder
MYLIB::Date dob; //date of birth of account holder
char address[100]; //address of account holder
char balance[20]; //balance of account holder
};
I don't know about the MYLIB::Date class, but it's enough that you have a std::string object in there.
The std::string object allocates memory dynamically to fit the string it contains. And memory allocated on the heap is available only to the current process, you can't save a pointer (which is inside the std::string class) and load it from some other process and hope there will be valid memory at that pointer.
If you save a pointer to dynamically allocated memory in one process, and load and use it from another process then you will have undefined behavior.
You need to serialize the string in order to save it. Possible the MYLIB::Data object as well.
Disclaimer: It will work on small embedded systems with a single unified address map, unfortunately all the bid user-oriented operating systems (like Windows, OSX and Linux) have separate address-spaces and walls between processes.
Your function AccountManagement::output() gives the impression it works perfectly, if you save the object and load it again in the same object and provided the string hasn't changed in the meantime.
What's wrong ?
As soon as your object is no longer a POD object (i.e. it contains data that use pointers, or use virtual functions, etc...), you can't just save it just by writing its memory to the disk.
In your case, the second function fails for this reason. The first function only gives the impression that it works. The string is a complex object that stores somewhere pointers to dynamically allocated memory. If you write the object and read it back as you did, without changing the object, the values that are in memory are simply re-read. The value of the hidden pointer that is read is exactly what it was before the read. That's a very lucky situation. But in most cases it will fail.
How to solve it ?
To save your object, you should serialize it: write/reade each component to the file separatly, using an appropriate function.
THe easiest way to do this is to use some existing serialisation libraries, such as boost serialization.

Simple File processing in C++

Trying to read a record of students with their name and numbers.
Writing to the file seems fine. However, reading from it prints a never-ending output. The statement - while(!file.eof()) - is causing the problem. But it's how I read the remaining person_details. Your help would be greatly appreciated.
#include <iostream>
#include <fstream>
#include <string>
using std::cin;
using std::cout;
using std::endl;
using std::fstream;
using std::string;
using std::ios;
class telephone
{
protected:
string name;
int number;
public:
void getDetails();
void printDetails() const;
};
void telephone:: getDetails()
{
cout<<"Enter name : "; getline(cin,name);
cout<<"Enter number : ";cin>>number;
}
void telephone:: printDetails() const
{
cout<<"Name : "<<name<<endl;
cout<<"Number : "<<number<<endl;
}
int main(int argc, char const *argv[])
{
telephone person;
fstream file("telefile.txt",ios::in | ios::out | ios::binary | ios::app);
if (!file)
{
cout<<"Invalid file name."<<endl;
return 1;
}
//writing
char choice;
do{
cout<<"----------"<<endl;
cout<<"Person : "<<endl;
cout<<"----------"<<endl;
person.getDetails();
file.write(reinterpret_cast<char*>(&person),sizeof(person));
cout<<"Enter one more?";
cin>>choice;cin.ignore();
}while(choice == 'y');
//reading
file.seekg(0);
file.read(reinterpret_cast<char*>(&person),sizeof(person));
while(!file.eof())
{
cout<<"----------"<<endl;
cout<<"Person : "<<endl;
cout<<"----------"<<endl;
person.printDetails();
file.read(reinterpret_cast<char*>(&person),sizeof(person));
}
return 0;
}
Your basic problem is that you're making a binary image of a std::string object, but a string object doesn't contain the character data, only a pointer to it (this is what allows a string to vary in size). So when you read that pointer back in, you'll start accessing whatever is in that memory location now, not what that memory held when you wrote the file. oops. Even worse things will happen when the string destructor runs and tries to free that pointer.
What you should do instead is write person.name.size() to the file, followed by that many bytes starting at &person.name[0]. Then when you read in the size, you can person.name.resize(size_from_file) and then read that many bytes into &person.name[0].
The Standard actually has a formal name for data types you are allowed to take binary images of: trivially copyable. The requirements are set out in section 9 and your telephone type doesn't meet them:
A trivially copyable class is a class that:
has no non-trivial copy constructors,
has no non-trivial move constructors,
has no non-trivial copy assignment operators,
has no non-trivial move assignment operators, and
has a trivial destructor.
The compiler-generated special member functions for telephone are non-trivial because of the std::string member, so instead of meeting all five requirements, you don't meet any of them.
Everyone has mentioned while (!file.eof()). You're not actually doing it wrong, but there is room for improvement.
First, you can catch more errors using while (file.good()) instead. Right now if you have any failure other than EOF, your loop never terminates, which matches your symptom.
Next, streams have a conversion to bool which is equivalent to calling good(), so you can write while (file). Finally, read like most other stream I/O functions returns the original stream, so you can write
while (file.read(buffer, size))
and avoid duplicating the read call both above and inside the loop.
But you didn't fall into the very common trap of checking eof() before doing the read that actually ran into the end. Bravo for that.