1

I am trying to figure out how I can update the database after adding or deleting a record. I used fprintf statements in the add and delete record functions which let me know the element data is being actually being added, which for some reason the add will work (delete record doesn't), but when I go to print the database, it's not printing the new database. How can I update the database and print it accordingly? Do I need a global variable to take in the new database?

Header file:

 #ifndef myStruct
 #define myStruct

struct creditCard
{
 char firstName[100]; 
 char lastName[100];
 char cardNumber[17]; //TA advised to use string since 16 digits will overflow in plain integer
 char expMonth[10];
};

//function headers below
int printCreditCard(struct creditCard *);

int sizeOfDb(struct creditCard *);

int countRecords(struct creditCard *);

int deleteRecord(struct creditCard *);

int addRecord(struct creditCard *);

 #endif

main program:

 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include "myStruct.h"

int accesses = 0; //number of times database was manipulated
int count = 0;  //used to count how many records there are; we always start at 4
//int size = 0; //used to tell us how big the database is
struct creditCard *ptr;  //chunk of memory for each record/structure
struct creditCard *headPtr; //chunk of memory for the first records/structure

int main(void)
{
    ptr = (struct creditCard *) malloc(4 * sizeof(struct creditCard));
    headPtr = ptr;

memcpy(ptr->firstName,"Bob",100);
memcpy(ptr->lastName,"Marley",100);
memcpy(ptr->cardNumber,"0000000000000000",17);
memcpy(ptr->expMonth,"September", 10);
ptr++;
count = count+1;

memcpy(ptr->firstName, "Will", 100);
memcpy(ptr->lastName, "Smith", 100);
memcpy(ptr->cardNumber,"1111111111111111",17);
memcpy(ptr->expMonth, "October", 10);
ptr++;
count = count+1;

memcpy(ptr->firstName, "Bill", 100);
memcpy(ptr->lastName, "Gates", 100);
memcpy(ptr->cardNumber,"2222222222222222",17);
memcpy(ptr->expMonth, "January", 10);
ptr++;
count = count+1;

memcpy(ptr->firstName, "Steve", 100);
memcpy(ptr->lastName, "Jobs", 100); 
memcpy(ptr->cardNumber,"3333333333333333",17);
memcpy(ptr->expMonth, "May", 10);
count = count+1;

while (1)
{
//  headPtr = ptr;  //put the newest database into headPtr so it points to the first record in  that database
//  ptr = headPtr;  //start the database back at the first record
//countRecords(ptr); //update the count of the current database

    int sel;
    printf("MAIN MENU\n");
    printf("=====\n");
    printf("1.  Select 1 to print all records.\n");
    printf("2.  Select 2 to print number of records .\n");
    printf("3.  Select 3 to print size of database.\n");
    printf("4.  Select 4 to add record.\n");
    printf("5.  Select 5 to delete record.\n");
    printf("6.  Select 6 to print number of accesses to database.\n");
    printf("7.  Select 7 to Exit.\n");
    printf("Enter Your Selection:  \n");

    scanf("%d", &sel); //get user input;

    if (sel == 1)
{   
    printCreditCard(ptr);
    accesses++;
}
else if (sel == 2)
{
    fprintf(stderr,"Number of records in the database is %d records\n", count); //pulls value of count from global updated variable
        accesses++;

}
else if (sel == 3)
{
    sizeOfDb(ptr);
    accesses++;
}
else if (sel == 4)
{
    ptr = headPtr;
    addRecord(ptr);
    accesses++;
}
else if (sel == 5)
{
    deleteRecord(ptr);
    accesses++;
}
else if (sel == 6)
{
    fprintf(stderr,"Number of accesses to the database is %d\n", accesses);
    accesses++;
}
else if (sel == 7)
{
            printf("Now Exiting.\n");
    return 0;
}
else
{
    printf("Invalid input, please select a valid option.\n");
    break; //go back to the main menu
}
}
}

//functions defined below
int sizeOfDb(struct creditCard *card2)
{
int size = 0;
int j;
    for (j = 1; j <= count; j++) 
    {
        size += sizeof(card2->firstName); //get the size of each element
    size += sizeof(card2->lastName); 
    size += sizeof(card2->cardNumber);
    size += sizeof(card2->expMonth);
    card2++;
    }
    //loop through each record and get sizeof() of each record
fprintf(stderr, "Total Size of the Database is %d bytes.\n", size);
return size;
}

int addRecord(struct creditCard *card3)
{
  char fName[100];
  char lName[100];
  char number[17];
  char month[10];

count = count+1;
fprintf(stderr, "count is %d \n", count);
int p;
struct creditCard *tempStruct;
struct creditCard *tempHead;
tempStruct = (struct creditCard *) malloc (count * sizeof(struct creditCard)); //allocate memory to a dummy record
tempHead = tempStruct;  
card3 = headPtr; //start at the beginning of the old database
for (p = 1; p < count; p++) //copies the old database in the new database up to the record before the newly allocated record
{ 
    memcpy(tempStruct->firstName, card3->firstName, 100);
    memcpy(tempStruct->lastName, card3->lastName, 100);
    memcpy(tempStruct->cardNumber, card3->cardNumber, 17);
    memcpy(tempStruct->expMonth, card3->expMonth, 10);
    fprintf(stderr, "first name is %s\n", tempStruct->firstName);
    if (p == count-1)  //if we are on the record before the last record, then only increment    tempStruct, card3 has one less record than the new database has
    {
        tempStruct++;
    }
    else
    {
        tempStruct++;
        card3++;
    }
}
printf("Please enter your first name.\n");
scanf("%s", fName);
memcpy(tempStruct->firstName, fName, 100);
    printf("Please enter your last name.\n");
    scanf("%s", lName);
    memcpy(tempStruct->lastName, lName, 100);//put first name in struct
    printf("Please enter your 16 digit credit card number with no spaces.\n");
scanf("%s", number);
    memcpy(tempStruct->cardNumber, number, 17);//put first name in struct
    printf("Please enter the month in which your credit card expires.\n");
    scanf("%s", month);
    memcpy(tempStruct->expMonth, month, 10);//put first name in struct
fprintf(stderr, "tempStruct first name is %s\n", tempStruct->firstName);
fprintf(stderr, "tempStruct last name is %s\n", tempStruct->lastName);
fprintf(stderr, "tempStruct card number is %s\n", tempStruct->cardNumber);
fprintf(stderr, "tempStruct exp month is %s\n", tempStruct->expMonth);

tempStruct = tempHead;
card3 = tempStruct;  //put the new database in place of the old database

//free(tempStruct); //clear memory for the dummy record because we don't need it anymore
return 0;
}

int deleteRecord(struct creditCard *card4)  //goes to the last record in the database and clears    the memory for it, essentially deleting it
{
int b;

count = count-1;
int l;
struct creditCard *newDb;  //will hold the new database with one less record at the end
struct creditCard *newDbHead;
newDb = (struct creditCard *) malloc(count * sizeof(struct creditCard));
newDbHead = newDb;
for (l = 1; l <= count; l++)
{ 
    memcpy(newDb->firstName,card4->firstName,100);
    memcpy(newDb->lastName,card4->lastName,100);
    memcpy(newDb->cardNumber,card4->cardNumber,17);
    memcpy(newDb->expMonth,card4->expMonth,10);
    if (l == count)
    {
        continue;
    }
    else
    {
        card4++;
        newDb++;
    }
}
newDb = newDbHead;  //start back at the first record
for (b = 1; b <= count; b++)
{
    fprintf(stderr, "first name for the current record is %s\n", newDb->firstName);
    newDb++;
}
card4 = newDb;  //put the new database into ptr to hold as the new database
return 0;
} 

int printCreditCard(struct creditCard *card)
{
 card = headPtr;  //start at the beginning of the database
 int i;
 if (count == 0)
{
printf("The database is empty\n");
return 0;
}
else
{
for (i = 1; i <= count; i++)
{
        printf("Credit Card Record %d\n", i);
    fprintf(stderr, "First Name = \%s\n", card-> firstName);
            fprintf(stderr, "Last Name = \%s\n", card-> lastName);
            fprintf(stderr, "Card Number = \%s\n", card-> cardNumber);
            fprintf(stderr, "Expiration Date = \%s\n\n", card-> expMonth);
    card++;  //go to the next record to print
 }
}
 return 1; //we have now printed all records, go to main menu.
}
jpw
  • 44,361
  • 6
  • 66
  • 86
ThomasM
  • 47
  • 7

2 Answers2

4

It's quite large sample that you provided, but for one, this:

memcpy(ptr->firstName, "Bill", 100);

doesn't seem ok to me. You specify size 100, but the character array "Bill" is 5 bytes long (1 for null terminator). So I think it will try to read past the array. You may try strcpy in this case, which will copy exactly 5 bytes.

Also as pointed by Serge Ballesta, you have similar problem inside sizeofDb method. You should set pointer to the start of the database:

int sizeOfDb(struct creditCard *card2)
{
      card2=headPtr;
      ...

Otherwise it is unclear what you are dereferencing afterwards. Actually, the reason it doesn't crash inside sizeofDb later, is due to this: Is dereferencing null pointer valid in sizeof operation. But logically, it should still point at the beginning.

Community
  • 1
  • 1
Giorgi Moniava
  • 27,046
  • 9
  • 53
  • 90
2

If I have correctly understood your logic, the database is an array of struct, and when you add or remove a record, you allocate a new array, copy the relevant records to new array, and for an addition copy the new record.

All that is fine (even if not very efficient), but it seems you never make the global variable headPtr point to the new array. You simply should free the old database and make headPtr point to new one.

You should have something like this at the end of addRecord and deleteRecord :

free(headPtr);
headPtr = tempHead; /* for addRecord */

headPtr = newDbHead; /* for deleteRecord */

And independently of the problem (should not be fatal) noticed by georgem, your deleteRecord function is wrong : you copy records from old database from new without first pointing to first record. You should at least use :

int deleteRecord(struct creditCard *card4)  //goes to the last record in the database and clears    the memory for it, essentially deleting it
{
int b;

count = count-1;
int l;
struct creditCard *newDb;  //will hold the new database with one less record at the end
struct creditCard *newDbHead;
newDb = (struct creditCard *) malloc(count * sizeof(struct creditCard));
newDbHead = newDb;
ptr = headPtr;
for (l = 1; l <= count; l++)
{
    if (ptr == card4) ptr++;
    /* no use to copy element by element : just copy full struct
    memcpy(newDb->firstName,ptr->firstName,100);
    memcpy(newDb->lastName,ptr->lastName,100);
    memcpy(newDb->cardNumber,ptr->cardNumber,17);
    memcpy(newDb->expMonth,ptr->expMonth,10); */
    /* memcpy(newDb, ptr, sizeof(creditCard)); */
    /* even better, thanks to  Jonathan Leffler */
    *newDb++ = *ptr++;
}
newDb = newDbHead;  //start back at the first record
for (b = 1; b <= count; b++)
{
    fprintf(stderr, "first name for the current record is %s\n", newDb->firstName);
    newDb++;
}
card4 = newDb;  //put the new database into ptr to hold as the new database
free(headPtr);
headPtr = newDbHead;
return 0;
} 
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • Why `memcpy()` instead of `*newDb = *ptr;`? I agree with your 'copy whole structure instead of piecemeal', but you'd have to go back to the 80s to find a compiler that didn't do structure copying (the one I used in 1983 had it as a new feature). – Jonathan Leffler Oct 03 '14 at 21:46
  • Thank you guys sooooo much!!!!! I got it to work after all these hours i put into this!!! thank you so much!!! It works now!!!! I can't tell you guys how grateful I am right now! My professor barely helped me after asking for help many many times. Thank you! – ThomasM Oct 03 '14 at 22:56