-1

I'm doing some homework for a C++ class, I'm instructed to create a struct that holds data for 4 company divisions, and sales figures for each quarter. I'm to save the struct to a binary file then re-import it and read the data. Everything is actually running as expected, however after the output I get a RUN FAILED, exit code 1 error.

I initially thought it was due to an array out of bounds somewhere but looking back on the code I cannot find it.

 * Author: James Hartley
 * Created on March 27, 2019, 10:09 PM
 * Description: Gaddis Ch.12 Problem 11-12 Corporate Sales Data Output/Input
 */

#include <iostream>
#include <fstream>

using namespace std;

struct division {
    string name;
    int qtrSales[4];
};

void structToFile(string fileName, division* div, int arySize) {
    fstream fileObject;
    fileObject.open(fileName, ios::out | ios::binary);
    fileObject.write(reinterpret_cast<char *>(div), sizeof(division) * arySize);
    fileObject.close();
}

void fileToStruct(string fileName, division* div, int arySize) {
    fstream fileObject;
    fileObject.open(fileName, ios::in | ios::binary);
    fileObject.read(reinterpret_cast<char *>(div), sizeof(division) * arySize);
    fileObject.close();
}

int main(int argc, char** argv) {
    division divs[4];
    division divsImport[4];
    divs[0].name = "East"; divs[1].name = "West"; divs[2].name = "North"; divs[3].name = "South";


    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 4; j++) {
            cout << "Please enter Quarter " << j+1 << " sales for " << divs[i].name << " division" << endl;
            cin >> divs[i].qtrSales[j];
        }
    }

    structToFile("struct.dat", divs, 4);
    fileToStruct("struct.dat", divsImport, 4);

    for (int i = 0; i < 4; i++) {
        cout << "Division: " << divsImport[i].name << endl;
        for (int j = 0; j < 4; j++) {
            cout << "Quarter: " << j+1 << endl;
            cout << "Sales: " << divsImport[i].qtrSales[j] << endl;
        }
    }
    return 0;
}

It outputs correctly, however tells me RUN FAILED exit code 1 after output.

MobiSlick
  • 13
  • 2
  • 1
    This may help: https://stackoverflow.com/questions/523872/how-do-you-serialize-an-object-in-c – drescherjm Mar 28 '19 at 17:46
  • 2
    `fileObject.write(reinterpret_cast(div), sizeof(division) * arySize);` -- You should realize this could never work. The `sizeof()` is a compile-time value, giving you the number of bytes the type consists of. So `sizeof(division)` remains the same, regardless of the number of characters in the `std::string` object. You could have a thousand characters in the string, `sizeof(division)` would remain the same. So you would have writing (maybe) 40 or so bytes of data to the file, no matter how many characters your `std::string` member has within it. – PaulMcKenzie Mar 28 '19 at 17:50
  • @PaulMcKenzie thank you for the detailed answer, you've made it very easy to understand what I was doing wrong. I appreciate all the help. – MobiSlick Mar 28 '19 at 18:29

1 Answers1

4

Your struct division contains an std::string, which is not a trivially-copyable type. An std::string will generally contain pointers to dynamic memory allocations which are only valid for as long as the specific string object lives. You cannot simply write the bits that make up an std::string object to disk, read it back into an std::string object and expect to have a valid std::string.

Michael Kenzel
  • 15,508
  • 2
  • 30
  • 39
  • 2
    And this is precisely why `reinterpret_cast` is the wrong tool for the job. That's the point where everything went wrong in the code. – Max Langhof Mar 28 '19 at 17:43
  • Thank you so much for the string explanation! I just changed string to char* and it works perfectly. Also, reinterpret_cast is what it tells me to use in my book. Gaddis C++ 8th edition, what other options are better? Im aware this books probably a little outdated – MobiSlick Mar 28 '19 at 17:51
  • 1
    @JamesHartley -- Changing to `char *` does not fix the problem. Maybe you should explain what you actually mean by "changing to `char *`". A `char *` is a 4 (or 8) byte value -- it isn't an array of `char`. – PaulMcKenzie Mar 28 '19 at 17:52
  • instead of storing the division names in a string I changed it to a char pointer and now it runs successfully and saves the data correctly. ```struct division { char* name; int qtrSales[4]; };``` – MobiSlick Mar 28 '19 at 17:54
  • 1
    @JamesHartley No, that cannot work. You are writing the value of a pointer to a file. Did you see the comment in the main section and the link to object serialization? A pointer is usually 4 or 8 bytes in size, so `sizeof(division)` gets you into the same issue as pointed out by the answer. If anything, you are just lucky you are seeing something that looks like it works. If you need proof, have that pointer point to a buffer that has a 100 character array. You will not see that array of characters in your file. – PaulMcKenzie Mar 28 '19 at 17:55
  • Your right, looking back at the output the division names are whacky, I just got overly excited when I saw a RUN SUCCESSFUL. I will check the link to object serialization – MobiSlick Mar 28 '19 at 17:59
  • 1
    Yes, you need to serialize the data. In other words, write the *data* that the `struct` represents, not the `struct` itself. – PaulMcKenzie Mar 28 '19 at 18:00
  • @JamesHartley Assuming your strings don't all have a fixed length, how do you know how long the `char` array is that you have to write/read from the file? – Michael Kenzel Mar 28 '19 at 18:01
  • Edited the program so it stores an array of char's now at a fixed length and I get the names through cin since all the names were different lengths, definitely not the most elegant solution but the program works now and It's enough for my class. Thank's everyone for all the help. I will definitely look into more solutions for serializing data in the future and I at least know a little more than I did before. I appreciate all the support. – MobiSlick Mar 28 '19 at 18:09