Why should "return 0" at the end of main cause crash ?

HI Everyone !
I faced something strange today ;
This is my code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include "StudentFuncs.h"

int main ()
{
    fstream stubin ("stubin.txt" , ios :: out | ios :: binary) ;
    vector <Student> stus = ReturnStusBinary () ;
    stubin.write ((char *) & stus , sizeof (stus)) ;
    stubin.close () ;
    fstream stubinin ("stubin.txt" , ios :: in | ios :: binary) ;
    vector <Student> stusin ;
    stubinin.read ((char *) & stusin , sizeof (stus)) ;
    //vector <Student> :: iterator it ;
    /*for (it = stusin.begin () ; it != stusin.end () ; it ++)
    {
        cout << (* it).GetFirstName () << endl ;
    }*/
    for (int i = 0 ; i < 5 ; i ++)
    {
        cout << stusin [i].GetFirstName () << endl ;
    }
    stubinin.close () ;
    cout << "TEST" ;
    return 0 ;
    cout << "TEST" ;
}


What it does is that it reads the information of some students from a (txt) file and writes them into another (bin) file .
The reason I'm writing this is that I'm just learning how to work with binary files (and I learnt)
BUT the problem is my program ran successfully , I mean it wrote all the first names of students (that means it could write the information in a binary file and could read them back) , but after writing their names , my program crashed .
I used output tests ( like cout << "TEST" ;) in every single line and ended up with that code I just wrote for you , in the output program , I see just one "TEST" and then the program crashes .
I have read that strings have problems with "read" and "write" functions and my class "Student" is full of strings , do you think that may cause it?


SIMPLY SPEAKING : In the code above I see just one TEST and the program crashes .


THNX!
You can't simply write a vector's binary form directly to file and read it back like that. That's very likely screwing up its internal state; it could crash anywhere after that, it just is probably crashing when it is being destructed on the return.
Nothing after return 0; will or should ever be executed. return is an instruction to exit the function, and go back to the place where the function was called from. In this case your main function returns to part of the C++ library which called your main function.

So you are supposed to see only one "Test" printed. Your compiler ought to warn you about having something after return (which can never be executed).
Last edited on
Nothing after return 0; will or should ever be executed.


atexit() stuff and destructors are run.

Specifically... vector's destructor is where the crash is happening because of what Zhuge said: you can't write to a vector object directly like that.... it's corrupting memory.


EDIT:

Personally.. i'm of the opinion that you should never do a raw read/write of anything but individual bytes.

See this article:

http://www.cplusplus.com/articles/DzywvCM9/
Last edited on
Ok, yeah, you're right. But nothing after in the function you're returning from will execute.
Last edited on
The problem is that you making a shallow copy of the vector. Even though you are doing it via a file, what you are doing is effectively:

memcpy((char*)&stusin, (char*)&stus, sizeof(vector<Student>));

Now, vectors use heap storage for their elements, to they have a pointer to heap memory somewhere inside (the details are implementation specific) pointing at the block of memory where the elements are stored. By making a shallow copy you have ended up with two vectors which think they own the same block of (heap) memory.

So when the first vector's destructor fires is cleans up ok; but when the second destructor fries, the memory has already been freed and things go wrong.

(With the debug build of Visual C++ I get an assert telling me the heap block I'm trying to free isn't in use.)

You do understand that by writing the vector's body to disk that you haven't actually stored the data the vector owns? (The data stored on the heap.)

Andy

PS And you should use .txt for binary files.
Last edited on
@Andy @Zhuge @Disch and @hitirwin THNX!
I read that article Disch gave it's link .
I almost understood why my program crashed .
So what can I do ?
According to that article I need to separate my vector to bytes and put them in file , (or something like that) but I have mop idea how to do that and how to read them back .
OR
I thought maybe I can create a temporary student object and assign all the students in vector to it one by one and for each one I write them to binary file .
'm not sure if it works , BUT I'm looking for a way to write the whole vector in file .
What should I do ?
Can I use other containers rather than VECTOR ?
THANK YOU ALL !
How is your Student class declared?

Andy
This is my student class :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "Person.h"

class Student : public Person
{
private :
    string ID ;
    string Certificate ;
    string Major ;
public :
    void SetID (string input) {ID = input ;}
    void SetCertificate (string input) {Certificate = input ;}
    void SetMajor (string input) {Major = input ;}
    string GetID () {return ID ;}
    string GetCertificate () {return Certificate ;}
    string GetMajor () {return Major ;} ;
    virtual string returnInfo () {return "\n" + GetFirstName () + "\n" + GetLastName () + "\n" + GetID () + "\n" + GetCertificate () + "\n" + GetMajor () + "\n" + GetDateRegedString () + "\n" ;}
} ;


and this is Person class :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include "Date.h"

class Person
{
private :
    string FirstName ;
    string LastName ;
    Date DateReged ;
public :
    void SetFirstName (string input) {FirstName = input ;}
    void SetLastName (string input) {LastName = input ;}
    void SetDateReged (int Dayin , int Monthin , int Yearin) {DateReged.SetDate (Dayin , Monthin , Yearin) ;}
    string GetFirstName () {return FirstName ;}
    string GetLastName () {return LastName ;}
    string GetDateRegedString () {return DateReged.ToString () ;}
    Date GetDateReged () {return DateReged ;} ;
    virtual string returnInfo () {return "Information lost!!!" ;} ;
} ;


Thanks!
I just tried copying students in vector into a temporary object and it worked fine .
What do you mean by a temporary object? Does it also use std::string?

And how's you class Date declared?

Andy
You know my problem is solved and I have no problem with the binary file , I chose another way to do that .



By the way The class Date is a normal Date class :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Date
{
private :
    int Day ;
    int Month ;
    int Year ;
public :
    Date () {} ;
    Date (int D , int M , int Y) {Day = D ; Month = M ; Year = Y ;}
    Date SetDate (int D , int M , int Y) {Day = D ; Month = M ; Year = Y ; return * this ;}
    string ToString () {return itos (Year) + "/" + itos (Month) + "/" + itos (Day) ;} ;
    void SetDay (int input) {Day = input ;}
    int GetDay (int input) {return Day ;}
    void SetMonth (int input) {Month = input ;}
    int GetMonth (int input) {return Month ;}
    void SetYear (int input) {Year = input ;}
    int GetYear (int input) {return Year ;}

} ;


By temporary object I mean this :
1
2
3
4
5
6
7
vector <Student> iterator :: it ;
Student tmp ;
    for (it = stus.begin () ; it != stus.end () ; it ++)
    {
        tmp = * it ;
        stubin.write ((char *) & tmp , sizeof (Student)) ;
    }


and for reading from file I use this :

1
2
3
4
5
6
7
8
9
stubin.read ((char *) & NStus , sizeof (int)) ;
for (int i = 0 ; i < NStus ; i ++)
    {
        stubin.read ((char *) & tmp , sizeof (Student)) ;
        cout << "Test" ;
        cout << tmp.GetFirstName () ;
cout << "Test" ;
        stus.push_back (tmp) ;
    }


Reading and Writing from/to a file is in two separate function .
I am facing a new problem :
in the ReadFromFile function I put two cout << "TEST"s and just one of them is shown , that means the read function can't read the objects from file from file . :|
ALSO this is the "ReturnStusBinary ()" Function if it helps :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
vector <Student> ReturnStusBinary () // This function is used to save the students info to a binary file
{
    fstream fstu ("student.txt" , ios :: in) ; // fstu = File Student
    string line ; // This is used for reading each line from the file
    vector <Student> stus ;
    Student tmp ;
    int NStus , Y , M , D ;
    char DS ;
    fstu >> NStus ;
    fstu.seekg (0) ;
    getline (fstu , line) ;
    for (int i = 0 ; i < NStus ; i ++)
    {
        getline (fstu , line) ;
        getline (fstu , line) ;
        tmp.SetFirstName (line) ;
        getline (fstu , line) ;
        tmp.SetLastName (line) ;
        getline (fstu , line) ;
        tmp.SetID (line) ;
        getline (fstu , line) ;
        tmp.SetCertificate (line) ;
        getline (fstu , line) ;
        tmp.SetMajor (line) ;
        fstu >> Y >> DS >> M >> DS >> D ;
        tmp.SetDateReged (D , M , Y) ;
        stus.push_back (tmp) ;
        getline (fstu , line) ;
    }
    fstu.close () ;
    return stus ;
}
The read problem relates to the way strings work with pointer internally.

The only reason your save sort of works is because (for performance reasons), most std::string implementations keep short strings in a buffer internal to the class and only allocate a separate buffer on the heap if more storage is required.

For the Visual C++ version of the class, std::string can use up to 16 chars before heap allocation is used, whereas it's just 8 for wchar_t wstrings, including the null terminator. (So you'd better hope Cherry Chevapravatdumrong's children don't turn up to enrole!)

But the buffer is always accessed though a pointer, which is initialized to point at the buffer, and then updated to point at heap memory later on, if additional storage space is required.

Now, a pointer has to point at a specific address. So if you copy from your binary file into a Student which is located in a different place in memory to when you saved the data, then you might put the string value into the new buffer, but you won't correctly set the pointer to point at this new buffer; the pointer value will still correspond to the location where the buffer was when you saved the student to file.

Andy
Last edited on
Topic archived. No new replies allowed.