1

This program that I am doing for class to create an EMPLOYEE records file. I created two structs. One called employee and one called date. The EMPLOYEE struct has one char array, one int, 6 float values and DATE(another Struct). The DATE struct has just three int values (month, day, year).

I have created an array of type EMPLOYEE called person[1000].

Here is my code, I keep getting a debug assertion failed error in visual studios pointing to fwrite.c Expression: (Stream !=NULL). I am sure it has to do with my fread or fwite as I have never tried to store structs.

The point of the beginning of this function is to populate the array if the file exists and is not empty, so when the user starts storing data to the array the subscript is updated. I know there are probably a few other issues here but first things first and that would be the reading and writing portion.

Thanks Again,

Mike

void loadPayRoll(EMPLOYEE person[], int *i) 
{
    char sValidate[5] = "exit";
    FILE *f;
    int count = 0;

    f = fopen("emplyeeRecords.bin", "rb");
    if(f){
        while(fread(&person[*i], sizeof(person), 1, f) > 0){
            (*i)++;
        }
        fclose(f);
    }
    else {

        while (strcmp( sValidate, person[*i].name)) {

            fopen("employeeRecords.bin", "ab+");
            printf("Please enter name or type exit to return to main menu: ");
            scanf("%s", person[*i].name);         //must use the '->' when passing by by refrence, must use '&' sign
            flush;

            if (!strcmp( sValidate, person[*i].name))
                break;

            printf("\nPlease enter age of %s: ", person[*i].name);
            scanf("%i", &person[*i].age);
            flush;
            printf("\nPlease enter the hourlyWage for %s: ", person[*i].name);
            scanf("%f", &person[*i].hourlyWage);
            flush;
            printf("\nPlease enter the hours worked for %s: ", person[*i].name);
            scanf("%f", &person[*i].hoursWkd);

            if (person[*i].hoursWkd > 40) {
                person[*i].regPay = person[*i].hoursWkd * 40;
                person[*i].otHoursWkd = person[*i].hoursWkd - 40;
                person[*i].otPay = person[*i].otHoursWkd * (person[*i].hourlyWage * 1.5);
                person[*i].totalPay = person[*i].regPay + person[*i].otPay;
            }
            else {
                person[*i].totalPay = person[*i].hoursWkd * person[*i].hourlyWage;
            }
            flush;
            printf("\nEnter 2 digit month: ");
            scanf("%i", &person[*i].payDate.month);   //must use the '->' when passing by by refrence, must use '&' sign
            flush;
            printf("\nEnter 2 digit day: ");
            scanf("%i", &person[*i].payDate.day);  //must use the '->' when passing by by refrence, must use '&' sign
            flush;
            printf("\nEnter 4 digit year: ");
            scanf("%i", &person[*i].payDate.year);   //must use the '->' when passing by by refrence, must use '&' sign 
            flush;
            fwrite(&person[*i], sizeof(person), 1, f);
            fclose(f);
            (*i)++;
        }
    }
}//end function loadPayRoll
WhozCraig
  • 65,258
  • 11
  • 75
  • 141
Michael Brooks
  • 489
  • 4
  • 15
  • 1
    Not sure why i is a pointer. It looks like you're using it as an index? It should probably be a regular integer. Other than that, please read here: http://stackoverflow.com/questions/3711233/is-the-struct-hack-technically-undefined-behavior – Thomas Dignan Feb 22 '13 at 21:52
  • There's no need to open and close the file inside the loop. Just open it before the loop and close it after. – Some programmer dude Feb 22 '13 at 21:54
  • i is a pointer because its being passed by another function, but now that I think about it. That might not be neccassary seeing as I dont think another function will need to know the ending subscript value. – Michael Brooks Feb 22 '13 at 22:18
  • @Micheal, even if you did need to know it, it'd be better to pass a regular integer as a copy and return its final value. – Thomas Dignan Feb 22 '13 at 22:33

1 Answers1

3

I'm pretty sure this:

fopen("employeeRecords.bin", "ab+");

sitting all by its lonesome on a single line without assigning the resulting FILE* has quite a bit to do with your problem.There are plenty of other issues for example:

flush;

Not really sure what thats all about. Perhaps you meant:

fflush(f);

Assuming f is ever actually assigned correctly,

And as pointed out in-comment, you should open the file before the loop starts, then write the data as needed, the close it after the loop is finished.

WhozCraig
  • 65,258
  • 11
  • 75
  • 141
  • Yes, I forgot to put f = fopen(). Flush is defined at the top of my program. – Michael Brooks Feb 22 '13 at 22:15
  • While I have your attention. Can I write the struct to the binary file in one shot even if I want to read certain values from the struct later on in the program? In another function I want to be able to pull only all of my float person[i].totalPay values for each instance of person. – Michael Brooks Feb 22 '13 at 22:16
  • @MichaelBrooks I would not advise it no matter what. Eventually you're going to need to get into the practice of properly packing and unpacking data int a *protocol* buffer of bytes. When you mem-write structs you risk differences in endian-ness, packing, alignments, etc, whenever the data is read back in. By defining a set of write/read functions that properly (and *portably*) write and read your data to a target file stream, your going to be better off in the long run. – WhozCraig Feb 22 '13 at 22:22
  • When you say that you woul not advise it. Are you referring to writing the struct in one shot? Meaning it would be preferred to write each individual value one at a time. I only ask so I can at least get on the right track. I am currently looking into big and little endian now but I have also never heard of it until now. – Michael Brooks Feb 22 '13 at 22:54
  • @MichaelBrooks meaning it would be preferable to define the precise binary image (byte by byte) that you want you file "object" to look like with regards to how those bytes should "fit" into your structure, and use that as your file image. Step out of the box of thinking your `struct` is that image on-disk. If it helps, consider an `int32_t` value written to disk as 4-bytes, taken from the value in big-endian format. Reading it back would be reading four bytes (0x01020304) and reassembling the value. Just a suggestion, It comes from years of having to write multi-platform portable code. – WhozCraig Feb 22 '13 at 23:04
  • Thank you, I may not have yet mastered memory location and byte allocation but Im much better off than I was earlier today. Being able to watch the binary file increase by 60 bytes after a struct was written to file really helped. My person struct uses exactly 60 bytes. My last question if you are still following this: – Michael Brooks Feb 23 '13 at 05:28
  • @MichaelBrooks I'm still following, what was it? – WhozCraig Feb 23 '13 at 05:30
  • If I wanted to pull specific data from all of the entries on the file, what would be the best way of going about it? I know the data I want is between bytes37-40, and 97-100, 157-160 etc...Im guessing I would use an fseek but I think I would need to loop through and increment the position by 60 bytes everytime. Am I even headed in the right direction here? Thanks again. – Michael Brooks Feb 23 '13 at 05:36
  • 1
    @MichaelBrooks That is the nice part of using fixed-length records. You can literally walk through the file, one seek after another, to each offset of each record where the data you want is. read it, then move on to the next, etc. You can't do that with variable length data (i.e why XML makes a putrid file-based database). You're seeing it right. Sounds like you understand. But you would increment by 56 bytes every time if you record is 60 (since you just finished advancing by 4 when you read that 32bit value, think about that a min.). – WhozCraig Feb 23 '13 at 05:53