Because it won't even run as posted because of this
1 2 3
|
foo.cpp: In function ‘int main()’:
foo.cpp:22:29: warning: ‘weatherData’ is used uninitialized in this function [-Wuninitialized]
getWeatherData(weatherData); //get user input into weather objects and add to vector
| |
I changed it to a regular vector reference, and removed all the Winblows cruft.
It of course crashes as expected because the serialisation is broken.
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
|
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
struct Weather {
string name;
string year;
float high;
float low;
};
void getWeatherData(vector < Weather > &);
void writeWeatherData(vector < Weather > &);
void readWeatherData(vector < Weather > &);
int main()
{
cout << "sizeof Weather = " << sizeof(Weather) << endl;
cout << "sizeof String = " << sizeof(string) << endl;
cout << "sizeof float = " << sizeof(float) << endl;
vector < Weather > weatherData;
getWeatherData(weatherData); //get user input into weather objects and add to vector
writeWeatherData(weatherData); //write weather objects from vector to file
readWeatherData(weatherData); //read weather data OBJECTS from file and place on vector
}
void getWeatherData(vector < Weather > &WD)
{
//gets data from the user, stores it in a temporary weather object and then adds the object to the vector
bool more = true;
char choice = '\0'; //!! NOT NULL, NULL != '\0' (which is nul)
while (more) {
Weather tmpWeather;
cout << "Enter the name of the Month: ";
cin >> tmpWeather.name;
cout << "Enter High Temp for the month: ";
cin >> tmpWeather.high;
cout << "Enter Low Temp for the Month: ";
cin >> tmpWeather.low;
WD.push_back(tmpWeather);
cout << endl << "Enter E to Exit, M to add more: ";
cin >> choice;
if (choice == 'E')
more = false;
}
}
void writeWeatherData(vector < Weather > &WD)
{
//writes all weather objects from the vector to the output binary file
fstream outFile("weather.out", ios::out | ios::binary);
if (outFile) {
for (Weather weather:WD)
outFile.write(reinterpret_cast < char *>(&weather), sizeof(Weather));
}
outFile.close();
}
void readWeatherData(vector < Weather > &WD)
{
Weather weather;
WD.clear(); //remove all previous data from the vector
ifstream inFile("weather.out", ios::in | ios::binary);
if (inFile) {
while (!(inFile.eof())) {
inFile.read(reinterpret_cast < char *>(&weather), sizeof(Weather));
WD.push_back(weather);
inFile.peek(); //force file to see if it is at EOF
}
}
inFile.close();
}
$ g++ -Wall -std=c++11 -g foo.cpp
$ gdb -q ./a.out
Reading symbols from ./a.out...done.
(gdb) run
sizeof Weather = 72
sizeof String = 32
sizeof float = 4
Enter the name of the Month: September
Enter High Temp for the month: 55
Enter Low Temp for the Month: 44
Enter E to Exit, M to add more: M
Enter the name of the Month: April
Enter High Temp for the month: 44
Enter Low Temp for the Month: 33
Enter E to Exit, M to add more: E
*** Error in `./a.out': munmap_chunk(): invalid pointer: 0x00007fffffffdb50 ***
Program received signal SIGABRT, Aborted.
0x00007ffff74aa428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
54 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0 0x00007ffff74aa428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1 0x00007ffff74ac02a in __GI_abort () at abort.c:89
#2 0x00007ffff74ec7ea in __libc_message (do_abort=do_abort@entry=2, fmt=fmt@entry=0x7ffff7605ed8 "*** Error in `%s': %s: 0x%s ***\n") at ../sysdeps/posix/libc_fatal.c:175
#3 0x00007ffff74f9698 in malloc_printerr (ar_ptr=0x0, ptr=<optimised out>, str=0x7ffff7605f00 "munmap_chunk(): invalid pointer", action=<optimised out>) at malloc.c:5006
#4 munmap_chunk (p=<optimised out>) at malloc.c:2842
#5 __GI___libc_free (mem=<optimised out>) at malloc.c:2963
#6 0x0000000000401aac in Weather::~Weather (this=0x7fffffffdb30, __in_chrg=<optimised out>) at foo.cpp:6
#7 0x000000000040198c in readWeatherData (WD=std::vector of length 2, capacity 2 = {...}) at foo.cpp:65
#8 0x0000000000401569 in main () at foo.cpp:26
| |
Pay CLOSE attention to the address in the dtor.
1 2 3 4 5 6 7 8 9 10 11
|
$ hd weather.out
00000000 30 db ff ff ff 7f 00 00 09 00 00 00 00 00 00 00 |0...............|
00000010 53 65 70 74 65 6d 62 65 72 00 00 00 00 00 00 00 |September.......|
00000020 50 db ff ff ff 7f 00 00 00 00 00 00 00 00 00 00 |P...............|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000040 00 00 5c 42 00 00 30 42 30 db ff ff ff 7f 00 00 |..\B..0B0.......|
00000050 05 00 00 00 00 00 00 00 41 70 72 69 6c 00 62 65 |........April.be|
00000060 72 00 00 00 00 00 00 00 50 db ff ff ff 7f 00 00 |r.......P.......|
00000070 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000080 00 00 00 00 00 00 00 00 00 00 30 42 00 00 04 42 |..........0B...B|
00000090
| |
this=0x7fffffffdb30 is what the code yanked out of the file and then tried to use it as if it were a real address. It was real, but ONLY at the moment you wrote it to the file.
At any other time, it's meaningless garbage and folly to try and use it as an address.
Compare with
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 33 34 35 36 37 38 39 40 41 42 43 44
|
void writeWeatherData(vector < Weather > &WD)
{
//writes all weather objects from the vector to the output binary file
fstream outFile("weather.out", ios::out | ios::binary);
if (outFile) {
for (Weather weather:WD) {
size_t len = weather.name.length();
outFile.write(reinterpret_cast < char *>(&len), sizeof(len));
outFile.write(reinterpret_cast < const char *>(weather.name.c_str()), len);
outFile.write(reinterpret_cast < char *>(&weather.high), sizeof(weather.high));
outFile.write(reinterpret_cast < char *>(&weather.low), sizeof(weather.low));
}
}
outFile.close();
}
void readWeatherData(vector < Weather > &WD)
{
Weather weather;
WD.clear(); //remove all previous data from the vector
ifstream inFile("weather.out", ios::in | ios::binary);
if (inFile) {
size_t len;
while (inFile.read(reinterpret_cast < char *>(&len), sizeof(len))) {
char *tmp = new char[len];
inFile.read(reinterpret_cast < char *>(tmp), len);
weather.name = string(tmp, len);
inFile.read(reinterpret_cast < char *>(&weather.high), sizeof(weather.high));
inFile.read(reinterpret_cast < char *>(&weather.low), sizeof(weather.low));
WD.push_back(weather);
delete [] tmp;
}
}
inFile.close();
}
$ hd weather.out
00000000 09 00 00 00 00 00 00 00 53 65 70 74 65 6d 62 65 |........Septembe|
00000010 72 00 00 5c 42 00 00 30 42 05 00 00 00 00 00 00 |r..\B..0B.......|
00000020 00 41 70 72 69 6c 00 00 5c 42 00 00 30 42 |.April..\B..0B|
0000002e
| |
Aside from being a shorter file, you'll also notice it doesn't contain any raw memory pointers.
Sure it's a PITA to need 4 transactions per record rather than one, but there are no short cuts.
FWIW, if you were being serious about serialising a lot of data, then this recent thread is well worth a read.
http://www.cplusplus.com/forum/general/268402/
If you really want a single write() per weather data, then you HAVE to do this.
1 2 3 4 5 6
|
struct Weather {
char name[20];
char year[5];
float high;
float low;
};
| |
You're only allowed the elemental types, arrays of elemental types, or nested structs containing only elemental types. Pointers and class variables do not work with simple block write calls.