c++exceptionaccess-violation

Program closes before function returns to main. Exception thrown: read access violation. **_Val** was 0x19B6C345688


My displayRecord() function runs correctly. It will display the given record, then pause for a second, but then the program closes before the function returns to main().

After debugging, I received this error:

Exception thrown: read access violation.
_Val was 0x19B6C345688.

#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
using namespace std;

struct Item
{
    string description;
    int quantity;
    double wCost;
    double rCost;
    string dateAdded;
};

int addRecord();
int displayRecord();
int changeRecord();

int main()
{  
    cout << "Inventory Program" << endl;
    cout << "-------------------------------" << endl << endl;

    char again = 'y';
    int selection = 0;

    do 
    {
        cout << "MENU" << endl;
        cout << "----" << endl;
        cout << "1. Add a record" << endl;
        cout << "2. Display a record" << endl;
        cout << "3. Change a record" << endl;
        cout << "4. Exit Program" << endl << endl;

        do
        {
            cout << "Please enter a selection from the menu (1, 2, 3, or 4): ";
            cin >> selection;

            if (selection != 1 && selection != 2 && selection != 3 && selection != 4)
            {
                cout << "\nYou must enter a valid selection of either 1, 2, 3, or 4. Please try again.\n\n";
                
            }

        } while (selection != 1 && selection != 2 && selection != 3 && selection != 4);

        if (selection == 4)
        {
            cout << "Exiting Program..." << endl;
            cin.clear();
            return 0;
        }

        if (selection == 1)
        {   
            if (addRecord() == 1)
            {
                return 1;
            }
        }

        else if (selection == 2)
        {
            if (displayRecord() == 1)
            {
                return 1;
            }
        }

        else
        {
            if (changeRecord() == 1)
            {
                return 1;
            }
        }

        cout << "Return to menu? (Type Y for yes): ";
        cin >> again;

    } while (again == 'y' || again == 'Y');
    

    cout << "-------------------------------" << endl << endl;

    system("pause");

    return 0;

}

int displayRecord()
{
    fstream inventoryFile("inventory.dat", ios::in | ios::binary);
    if (!inventoryFile)
    {
        cout << "ERROR: Cannot open file. Aborting Program." << endl;
        return 1;
    }

    Item item;
    long recNum = 0;
    
    cout << "\nEnter the record number you wish to display: ";
    cin >> recNum;

    inventoryFile.seekg((recNum - 1) * sizeof(item), ios::beg);
    inventoryFile.read(reinterpret_cast<char*>(&item), sizeof(item));
    
    if (!inventoryFile)
    {
        cout << "\nERROR: Record does not exist." << endl << endl;
        inventoryFile.close();
        return 0;
    }

    else
    {
        cout << endl;
        cout << showpoint << setprecision(2) << fixed << endl;
        cout << "Item Description: " << item.description << endl;
        cout << "Quantity        : " << item.quantity << endl;
        cout << "Wholesale Cost  : " << item.wCost << endl;
        cout << "Retail Cost     : " << item.rCost << endl;
        cout << "Date Added:     : " << item.dateAdded << endl << endl;
        inventoryFile.clear();
    }
                  
    inventoryFile.close();

    return 0;
}

I have another branch where I open the file in main() and pass the file to the function, but the same thing happens.

I have used clear() and seekg() to set the position at the beginning of the file.


Solution

  • Your Item type is non-trivial, as it contains non-trivial std::string members. As such, you cannot use istream::read() and ostream::write() to read/write Item objects in binary mode the way you are doing.

    std::string contains a pointer to its character data, which may be (and usually is) located elsewhere in memory, outside of the std::string object. You are reading/writing the internal state of the std::string objects, not their character data. So, when you write a file, then restart the program, and then read the file, you will not end up with valid string objects.

    To read/write your items in binary mode, they need to be of a trivial type. And to facilitate the random seeking that your code is using, those items need to be a fixed size. Which means, you will have to use fixed-length char[] arrays for your strings, eg:

    struct Item
    {
        char description[256];
        int quantity;
        double wCost;
        double rCost;
        char dateAdded[32];
    };
    

    Otherwise, if you want to read/write std::string values then you must (de-)serialize the Item objects instead (which is not kind to random seeking), eg:

    #include <iostream>
    #include <fstream>
    #include <string>
    #include <iomanip>
    #include <cstdint>
    #include <cstdio>
    using namespace std;
    
    struct Item
    {
        string description;
        int quantity;
        double wCost;
        double rCost;
        string dateAdded;
    };
    
    template<typename T>
    istream& read(istream &in, T &value)
    {
        return in.read(reinterpret_cast<char*>(&value), sizeof(value));
    }
    
    template<typename T>
    ostream& write(ostream &out, const T &value)
    {
        return out.write(reinterpret_cast<const char*>(&value), sizeof(value));
    }
    
    istream& read(istream &in, string &value)
    {
        uint32_t size;
        if (in.read(reinterpret_cast<char*>(&size), sizeof(size)))
        {
            value.resize(size);
            in.read(value.data(), size);
        }
        return in;
    }
    
    ostream& write(ostream &out, const string &value)
    {
        uint32_t size = value.size();
        if (out.write(reinterpret_cast<char*>(&size), sizeof(size)))
            out.write(value.c_str(), size);
        return out;
    }
    
    istream& operator>>(istream &in, Item &item)
    {
        read(in, item.description);
        read(in, item.quantity);
        read(in, item.wCost);
        read(in, item.rCost);
        read(in, item.dateAdded);
        return in;
    }
    
    ostream& operator<<(ostream &out, const Item &item)
    {
        write(out, item.description);
        write(out, item.quantity);
        write(out, item.wCost);
        write(out, item.rCost);
        write(out, item.dateAdded);
        return out;
    }
    
    ...
    
    int addRecord()
    {
        Item item;
        ...
    
        ofstream inventoryFile("inventory.dat", ios::binary | ios::app);
        if (!inventoryFile)
        {
            cout << "ERROR: Cannot open file. Aborting Program." << endl;
            return 1;
        }
    
        if (!(inventoryFile << item))
        {
            cout << "ERROR: Cannot write to file. Aborting Program." << endl;
            return 1;
        }
    
        return 0;
    }
    
    int displayRecord()
    {
        ifstream inventoryFile("inventory.dat", ios::binary);
        if (!inventoryFile)
        {
            cout << "ERROR: Cannot open file. Aborting Program." << endl;
            return 1;
        }
    
        long recNum = 0;
        
        cout << "\nEnter the record number you wish to display: ";
        cin >> recNum;
    
        Item item;
        while (recNum > 1)
        {
            if (!(inventoryFile >> item))
            {
                cout << "\nERROR: Record does not exist." << endl << endl;
                return 0;
            }
            ---recNum;
        }
        
        if (!(inventoryFile >> item))
        {
            cout << "ERROR: Cannot read from file. Aborting Program." << endl;
            return 1;
        }
    
        cout << endl;
        cout << showpoint << setprecision(2) << fixed << endl;
        cout << "Item Description: " << item.description << endl;
        cout << "Quantity        : " << item.quantity << endl;
        cout << "Wholesale Cost  : " << item.wCost << endl;
        cout << "Retail Cost     : " << item.rCost << endl;
        cout << "Date Added:     : " << item.dateAdded << endl << endl;
    
        return 0;
    }
    
    int changeRecord()
    {
        ifstream inventoryFile("inventory.dat", ios::binary);
        if (!inventoryFile)
        {
            cout << "ERROR: Cannot open file. Aborting Program." << endl;
            return 1;
        }
    
        ofstream inventoryTemp("inventory.tmp", ios::binary);
        if (!inventoryTemp)
        {
            cout << "ERROR: Cannot create file. Aborting Program." << endl;
            return 1;
        }
    
        long recNum = 0;
        
        cout << "\nEnter the record number you wish to change: ";
        cin >> recNum;
    
        Item item;
        while (recNum > 1)
        {
            if (!(inventoryFile >> item))
            {
                cout << "\nERROR: Record does not exist." << endl << endl;
                inventoryTemp.close();
                remove("inventory.tmp");
                return 0;
            }
            if (!(inventoryTemp << item))
            {
                cout << "ERROR: Cannot write to file. Aborting Program." << endl;
                inventoryTemp.close();
                remove("inventory.tmp");
                return 1;
            }
            ---recNum;
        }
        
        if (!(inventoryFile >> item))
        {
            cout << "ERROR: Cannot read from file. Aborting Program." << endl;
            inventoryTemp.close();
            remove("inventory.tmp");
            return 1;
        }
    
        // display item...
        // ask user for new values...
    
        if (!(inventoryTemp << item))
        {
            cout << "ERROR: Cannot write to file. Aborting Program." << endl;
            inventoryTemp.close();
            remove("inventory.tmp");
            return 1;
        };
    
        while (inventoryFile >> item)
        {
            if (!(inventoryTemp << item))
            {
                cout << "ERROR: Cannot write to file. Aborting Program." << endl;
                inventoryTemp.close();
                remove("inventory.tmp");
                return 1;
            }
        }
    
        if (!inventoryFile.eof())
        {
            cout << "ERROR: Cannot read from file. Aborting Program." << endl;
            inventoryTemp.close();
            remove("inventory.tmp");
            return 1;
        }
    
        inventoryFile.close();
        inventoryTemp.close();
    
        remove("inventory.bak");
        rename("inventory.dat", "inventory.bak");
        rename("inventory.tmp", "inventory.dat");
    
        return 0;
    }