1

I am writing a basic program that must read and write information from a text file into a structure. The function that dumps all struct information into a text file works, but trying to scan the same information back in from the file into an empty structure keeps resulting in an Access Violation Error.

#include <stdio.h>
#include <stdlib.h>

#define MAXENTRIES 100

struct entry{
    unsigned int recordNum;
    char *toolName;
    unsigned int quantity;
    double cost;
};
struct entry dataBase[MAXENTRIES];
int currentEntries = 0;

void initDataBase(void){
    dataBase[0].recordNum = 3;
    dataBase[0].toolName = "Electric Sander";
    dataBase[0].quantity = 7;
    dataBase[0].cost = 57.98;
//    scanf("%d", &dataBase[0].recordNum);

    dataBase[1].recordNum = 17;
    dataBase[1].toolName = "Hammer";
    dataBase[1].quantity = 76;
    dataBase[1].cost = 11.99;

    dataBase[2].recordNum = 24;
    dataBase[2].toolName = "Jig Saw";
    dataBase[2].quantity = 21;
    dataBase[2].cost = 11.00;

    dataBase[3].recordNum = 39;
    dataBase[3].toolName = "Lawn Mower";
    dataBase[3].quantity = 3;
    dataBase[3].cost = 79.50;

    dataBase[4].recordNum = 56;
    dataBase[4].toolName = "Power Saw";
    dataBase[4].quantity = 18;
    dataBase[4].cost = 99.99;

    dataBase[5].recordNum = 68;
    dataBase[5].toolName = "Screwdriver";
    dataBase[5].quantity = 106;
    dataBase[5].cost = 6.99;

    dataBase[6].recordNum = 77;
    dataBase[6].toolName = "Sledge Hammer";
    dataBase[6].quantity = 11;
    dataBase[6].cost = 21.50;

    dataBase[7].recordNum = 83;
    dataBase[7].toolName = "Wrench";
    dataBase[7].quantity = 34;
    dataBase[7].cost = 7.50;

    currentEntries = 8;
}

void printEntry(int i){
    printf("\nRecordNum : %d", dataBase[i].recordNum);
    printf("\nTool Name : %s", dataBase[i].toolName);
    printf("\nQuantity  : %d", dataBase[i].quantity);
    printf("\nCost      : %.2f\n\n", dataBase[i].cost);
}

void fprintDataBase(void){
    FILE *dbPtr;
    if((dbPtr = fopen("hardware.dat", "w")) == NULL){
        puts("File could not be opened");
    }
    else{
        for(int i = 0; i < currentEntries; i++){
                fprintf(dbPtr, "%d %s %d %.2f\n", dataBase[i].recordNum, dataBase[i].toolName, dataBase[i].quantity, dataBase[i].cost);
        }
        printf("\nDatabase recorded!\n");
        fclose(dbPtr);
    }
}

void fscanDataBase(void){
    FILE *dbPtr;
    if((dbPtr = fopen("hardware.dat", "r")) == NULL){
        puts("File could not be opened");
    }
    else{
        currentEntries = 1;
//        fscanf(dbPtr, "%d%29s%d%lf", &dataBase[0].recordNum, dataBase[0].toolName, &dataBase[0].quantity, &dataBase[0].cost);
        fscanf(dbPtr, "%d", &dataBase[0].recordNum);       //Access Violation Error HERE

        while(!feof(dbPtr)){
            fscanf(dbPtr, "%d%29s%d%lf", &dataBase[currentEntries].recordNum, dataBase[currentEntries].toolName, &dataBase[currentEntries].quantity, &dataBase[currentEntries].cost);
            currentEntries++;
        }
        printf("\nDatabase scanned in!\n");
        fclose(dbPtr);
    }
}

int main()
{
//    initDataBase();
//    fprintDataBase();
    fscanDataBase();
    printEntry(0);

    return 0;
}

Running this program gets this output:


Process returned -1073741819 (0xC0000005)   execution time : 0.344 s
Press any key to continue.

Running initDataBase() and then fprintDataBase() correctly results in a file with everything I need, but commenting out those calls and calling fscanDataBase() results in the aforementioned error.

Since this is an Access Violation error, my first thought is that this is an array issue, but I was able to access dataBase[0] during initDataBase() with no trouble. This also means that it can't be an issue of the struct falling out of scope (which it shouldn't anyway, being a global).

As shown in one of the commented lines, I was able to perform a scanf into dataBase[0] during initDataBase(), so this shouldn't be an issue with my syntax.

All of this leads me to think this is a problem with how I'm using fscanf, which is beyond my current level of understanding.

  • 3
    Are you really sure it crashes on this first `scanf` ? Reading the code, I see a memory violation access in the `scanf` in the while loop, but the first one seems ok. In the while loop, there's no memory allocated for `toolname.` – Joël Hecht Jun 07 '22 at 05:27
  • 1
    Owen Gibson, `while(!feof(dbPtr)){` --> [`Why is “while ( !feof (file) )” always wrong?`](https://stackoverflow.com/q/5431941/2410359). Who or what text suggested using `feof()` this way? – chux - Reinstate Monica Jun 07 '22 at 05:43
  • The textbook "C - How to Program, 7th Edition by Paul & Harvey Deitel". This is nearly verbatim their example of reading data from a sequential access file. – Owen Gibson Jun 07 '22 at 17:49

1 Answers1

0

Your major issue is that there is no memory allocated for each entry.toolName. Without changing the structure, the smallest change I could make is to scan into a temporary buffer, then duplicate the string. Of course this has memory-cleanup issues that need to be handled.

void fscanDataBase(void)
{
    FILE *dbPtr;
    if ((dbPtr = fopen("hardware.dat", "r")) == NULL)
    {
        puts("File could not be opened");
    }
    else
    {
        currentEntries = 0;

        while(!feof(dbPtr))
        {
            char tempName[32];
            fscanf(dbPtr, "%d %29s %d %lf", &dataBase[currentEntries].recordNum,
                                             tempName,
                                            &dataBase[currentEntries].quantity,
                                            &dataBase[currentEntries].cost);

            dataBase[currentEntries].toolName = strdup(tempName);

            currentEntries++;
        }
        printf("\nDatabase scanned in!\n");
        fclose(dbPtr);
    }
}

But probably, you meant to allocate space in your struct entry:

struct entry
{
    unsigned int recordNum;
    char         toolName[32];    // <<-- HERE
    unsigned int quantity;
    double       cost;
};

Be careful about overwriting the length of the string buffer here. You'll lose marks if you don't handle all sorts of issues (like the return value from fscanf()).

You probably want to test your code with some rubbish input too. Your teacher/lecturer will.

Kingsley
  • 14,398
  • 5
  • 31
  • 53
  • Making these changes still results in the same error. While I admit my string memory handling is messy, the purpose of breaking up the first `fscanf` into just the first int read was to see if any read was causing the error or just the string scan. I found that any `fscanf` was producing that error, even the ones scanning an integer from the file into `&dataBase[0].recordNum`. – Owen Gibson Jun 07 '22 at 17:58
  • 1
    Got it working! Scanned everything into temporary values and set them to the struct variables afterwards. It seems that `fscanf` just doesn't like directly interacting with struct variables. – Owen Gibson Jun 07 '22 at 19:06