Search code examples
cmallocdynamic-memory-allocationrealloc

C Program Crashing when Reallocing One Element Larger (2x)


I am a beginner to C and am trying to learn dynamic memory allocation in the simplest way - I have a program that manages student records and I have a few problems with it that I cannot for the life of me figure out even after hours and hours of googleing/stackoverflowing -- Here is what this program is to accomplish:

  • Take in student records
  • Add and Delete Records ( THESE ARE THE COMMANDS THAT DO NOT WORK)
  • Sort student records
  • Find a few extra values from these records

Here is what happens to create this error as found in GDB debugger (the problem comes with the addRecord function and the freeMemory function):

    *** Error in `/home/a.out': realloc(): invalid next size: 
    0x0000000000c42060 ***                                                        
    Aborted 

And when compiled with GCC and ran outside of the GDB debugger it will just seg fault.

This only happens when I do these set of options in the program:

1) ADD A RECORD

2) DELETE A RECORD

3) ADD A RECORD ( THEN CRASHES )

or

1) ADD A RECORD

2) ADD A RECORD ( THEN CRASHES )

I'm not sure whats causing this and I have no idea how to fix it, can anyone help direct me in the right direction? I've tried adding a free after the realloc success in the freeMemory function but to no avail this then causes the program to crash upon deletion of an added record.

Here is the code:

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

// Declare functions:
void printMenu();
void printRecords(char** fn, char** ln, float* s);
void addRecord(char** fn, char** ln, float* s);
void deleteRecord(char** fn, char** ln, float* s);
int freeMemory(char** fn, char** ln, float* s, char* n, int matches);
int findName(char** ln,char* n);


static int records; // global records variable

int main(){
    int i, j;
    int choice;
    int option = -1;

    printf("WELCOME TO THE STUDENT RECORD MANAGER 100 V 1.0! \n");
    printf("Please indicate the number of student records you want to enter (min 5, max 15): \n");
    scanf("%d",&records);

    if(records < 5 || records > 15){
        printf("You must enter more than five and less than 15... terminating. \n");
        return 0;
    }

    // Declare arrays
    char** firstNames;
    char** lastNames;
    float* scores;

    // Declare variables
    char* search = malloc(64);

    firstNames  =   malloc(records*sizeof(*firstNames));
    lastNames   =   malloc(records*sizeof(*lastNames));
    scores      =   malloc(records*sizeof(float));


    printf("Please enter the records now (ex. firstName lastName score ENTER):\n");
    // Gather Records
    for(i = 0; i < records; i ++){
        printf("%d ",i+1);
        firstNames[i] = malloc(254 * sizeof(char));
        lastNames[i] = malloc(254 * sizeof(char));
        //scores[i] = malloc(sizeof(float));
        scanf("%s %s %f",firstNames[i], lastNames[i], &scores[i]);
    }

    // Generate menu and do actions
    do{
        printMenu();
        scanf("%d",&choice);

        switch(choice){
            case 1:
                printRecords(firstNames, lastNames, scores);
                break;
            case 2:
                addRecord(firstNames, lastNames, scores);
                break;
            case 3:
                deleteRecord(firstNames, lastNames, scores);
                break;
            case 0:
                return 0;
        }
    }
    while(1);
    return 0;
}

// Print user menu
void printMenu(){
    printf("\tMain Menu\t\n"
        "============================\n"
        " > Print records (press 1) \n"
        " > Add a new record (press 2) \n"
        " > Delete a record (press 3) \n"
        " > Exit the program (press 0)\n"
        "============================\n"
        "Please select an option: ");
}

// Print all user records
void printRecords(char** fn, char** ln, float* s){
    int i;
    printf("THERE ARE %d RECORDS \n",records);
    for(i=0;i<records;i++)
        printf("(%d) First name: %s | Last name: %s | Score: %0.2f \n",i,fn[i],ln[i],s[i]); // will start at the first item and go i amount in the index
}

// Add user record
void addRecord(char** fn, char**ln, float* s){
    char** tempPtr;
    float* tempFPtr;
    printf("Please input the values that you'd like to add (ex. firstName lastName score ENTER): \n");
    if(records+1 < 15){ // if the array is not larger than maximum value when we make it larger

        // lets reallocate the arrays
        tempPtr = realloc(fn, (records+1)*sizeof(*fn));
        if(tempPtr){
            printf("ALLOCATION successfully");
            fn = tempPtr;
        }
        else{
            printf("FAILED");
            // Realloc Failed
        }
        tempPtr = realloc(ln, (records+1)*sizeof(*ln));
        if(tempPtr){
            printf("ALLOCATION successfully");
            ln = tempPtr;
        }
        else{
            printf("FAILED");
            // Realloc Failed
        }
        /*tempFPtr = realloc(s, records+1*sizeof(float));
        if(tempFPtr){
            s = tempFPtr;
        }*/
        printf("There are now %d items.. \n", records);
        fn[records] = malloc(64); // in the LAST value of all arrays
        ln[records] = malloc(64);
        scanf("%s %s %f",fn[records], ln[records], &s[records]);
        printf("(%d) > ADDED -> First name: %s | Last name: %s | Score: %0.2f \n",records,fn[records],ln[records],s[records]);
        int i;
        records++; // increment by one
        printf("There are now %d items.. \n", records);
        printf(" > Record successfully added.\n");
    }
    else{
        printf(" > You have hit the max amount of records allowed for this program (15).\n");
    }

}

// Delete all instances of user record by (last Name)
void deleteRecord(char** fn, char**ln, float* s){
    char* lastName = malloc(32 * sizeof(char*));
    printf("What is the last name of the student you would like to delete? \n");
    scanf("%s", lastName);

    // searching for that record
    int matches = findName(ln, lastName);
    if(matches && records-matches > 4){ // this means it doesn't go below minimum 5
        if(freeMemory(fn, ln, s, lastName, matches)){
            printf(" > Record successfully deleted.\n");
            free(lastName);
        }
    }
    else{
        printf(" > Either no matches were found or deleting this value would put the number of records at less than 5. \n");
    }

}

// Extension of deleteRecord that does duplication to push 'delete' lastName to the end, then reallocates array to cut it off the end
int freeMemory(char** fn, char** ln, float* s, char* n, int matches){
    int i,j;

    /* LOGIC:
        - REALLOCATE ARRAY BY X LESS SIZE AFTER PUSHING THE VALUES TO BE REMOVED TO THE END
        - NEED TO DO THIS FOR THREE ARRAYS
        - TO REMOVE:
            - RUN THROUGH X AMOUNT OF TIMES PUSHING EACH SPECIFIC REMOVAL VALUE (IE) 1st found match
            - TO THE END
            SHIFT
                - FOUND 1st MATCH
                - MATCH = MATCH + 1
                - MATCH + ! = MATCH + 2
                - ETC UNTIL YOU HAVE A DUPLICATE AT THE END, THEN REALLOCATE BY 1 LESS
    */
    char** tempPtr = NULL;
    float* tempFPtr = NULL;
    for(i=0;i<matches;i++){ // gonna go through as many times as matches
        int match = 0;
        // let's make sure the name isn't on the end of the array before we do all this junk
        if(strcmp(n,*(ln+records-1)) == 0){
            printf("FN");
            tempPtr = realloc(fn, (records-1)*sizeof(*fn));
            if(tempPtr){
                fn = tempPtr;
                printf("FN PASSED");
                //free(fn[records]);
            }
            else{
                // Realloc Failed
                printf("FN FAILED");
            }
            printf("LN");
            tempPtr = realloc(ln, (records-1)*sizeof(*ln));
            if(tempPtr){
                ln = tempPtr;
                printf("LN PASSED");
                //free(ln[records]);
            }
            else{
                printf("LN FAILED");
                // Realloc Failed
            }

            records--;
        }
        else{
            // lets find the first match by going through each last name
            for(j=0;j<records-1;j++){
                // Lets find first match
                if(strcmp(n,ln[j]) == 0){ // accessing index
                    // lets start copying
                    *(ln+j) = *(ln+j+1);
                    *(fn+j) = *(fn+j+1);
                    *(s+j)  = *(s+j+1);
                    match = 1;
                }
                else if(match){
                    // already copied first val so lets start copying the rest until we get to the end.
                    *(ln+j) = *(ln+j+1);
                    *(fn+j) = *(fn+j+1);
                    *(s+j)  = *(s+j+1);
                }

            }
            printf("FN");
            tempPtr = realloc(fn, (records-1)*sizeof(*fn));
            if(tempPtr){
                fn = tempPtr;
                printf("FN PASSED");
                //free(fn[records]);
            }
            else{
                // Realloc Failed
                printf("FN FAILED");
            }
            printf("LN");
            tempPtr = realloc(ln, (records-1)*sizeof(*ln));
            if(tempPtr){
                ln = tempPtr;
                printf("LN PASSED");
                //free(ln[records]);
            }
            else{
                printf("LN FAILED");
                // Realloc Failed
            }

            /*tempFPtr = realloc(s, records-1*sizeof(float));
            if(tempFPtr){
                s = tempFPtr;
                printf("S PASSED");
            }
            else{
                printf("s FAILED ");
            }*/

            records--;
            match = 0;
        }
    }
    return 1;
}

// Finds how many instances of a last name are present
int findName(char** ln,char* n){
    int i, counter=0;

    for(i=0;i<records-1;i++){
        if(strcmp(n,*(ln+i)) == 0){
            counter++;
        }
    }
    return counter;
}

Solution

  • I have solved the problem. It was because I was not using triple pointers to it was not addressing the original variable.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void printMenu();
    void printRecords(char** fn, char** ln, double* s);
    void addRecord(char ***fn, char ***ln, double **s);
    void addString(char ***strArray, char* str);
    void addDouble(double** dubArray, double dub);
    void deleteRecord(char*** fn, char*** ln, double** s);
    void removeString(char ***strArray);
    void removeDouble(double** dubArray);
    int findName(char** ln,char* n);
    
    int records;
    int counter;
    
    int main(){
        records = 0;
        counter = 1;
    
        int initRecords = 0;
        int choice,i;
        char **fn; // first Names
        char **ln; // last Names
        double *s; // score
    
        printf("=  =\n"
               "Please enter how many value"
               "s you would like to initall"
               "y enter [min 5, max 15]: ");
        scanf("%d", &initRecords);
        if(initRecords < 5 || initRecords > 15)
            return 0;
    
        // allocate memory
        fn = malloc(0); // will reallocate later
        ln = malloc(0);
        s =  malloc(0);
    
        printf("Please enter the value (firstName lastName score ENTER): \n");
        for(i = 0;i<(initRecords);i++){
            addRecord(&fn, &ln, &s);
            counter++;
        }
    
        printf("Records added... \n");
        do{
            printMenu();
            scanf("%d",&choice);
            switch(choice){
                case 1:
                    printRecords(fn, ln, s);
                    break;
                case 2:
                    addRecord(&fn,&ln,&s);
                    printf("> Record added. \n");
                    break;
                case 3:
                    deleteRecord(&fn, &ln, &s);
                    printf("> Record deleted. \n");
                    break;
                case 0:
                    return 0;
                default:
                    return 0;
            }
        }
        while(1);
    }
    
    // Print user menu
    void printMenu(){
        printf("\tMain Menu\t\n"
            "============================\n"
            " > Print records (press 1) \n"
            " > Add a new record (press 2) \n"
            " > Delete a record (press 3) \n"
            "============================\n"
            "Please select an option: ");
    }
    
    // Print all user records
    void printRecords(char** fn, char** ln, double* s){
        int i;
        for(i=0;i<records;i++)
            printf("(%d) First name: %s | Last name: %s | Score: %0.2lf \n",i+1,fn[i],ln[i],s[i]); // will start at the first item and go i amount in the index
    }
    
    
    void addRecord(char*** fn, char*** ln, double** s){
        // gather value
    
        char* fname = malloc(64);
        char* lname = malloc(64);
        double score;
        records++;
    
        printf("%d) ", (counter));
        scanf("%s %s %lf",fname, lname, &score);
    
        addString(fn, fname);
        addString(ln, fname);
        addDouble(s, score);
    }
    
    void addString(char*** strArray, char* str){
        // realloc array one larger
        *strArray = realloc(*strArray, (records) * sizeof(char*));
    
        // set pointer equal
        (*strArray)[records-1] = str;
    
    }
    
    void addDouble(double** dubArray, double dub){
        // realloc array one larger
        *dubArray = realloc(*dubArray, (records) * sizeof(double));
    
        // set pointer equal
        (*dubArray)[records-1] = dub;
    }
    
    void deleteRecord(char*** fn, char*** ln, double** s){
        //This part gets the name
        int i, j;
        char *name = malloc(64);
        printf("Please enter the last name of the record(s) you'd like to delete: ");
        scanf("%s",name);
    
        while(findName(*ln, name) != 0){
            for(i = 0;i<records && strcmp(name,(*ln)[i]) != 0;i++);
            records--;
            //free((*ln)[i]);
            //free((*fn)[i]);
            for(i=i; i < records-1; i++){
                (*ln)[i] = (*ln)[i+1];
                (*fn)[i] = (*fn)[i+1];
                (*s)[i] = (*s)[i+1];
            }
            removeString(fn);
            removeString(ln);
            removeDouble(s);
        }
    
    }
    void removeString(char ***strArray){
        *strArray = realloc(*strArray, (records) * sizeof(char*));
    
    }
    void removeDouble(double** dubArray){
        *dubArray = realloc(*dubArray, (records) * sizeof(double));
    }
    
    // Finds how many instances of a last name are present
    int findName(char** ln,char* n){
        int i, counter=0;
    
        for(i=0;i<records-1;i++){
            if(strcmp(n,*(ln+i)) == 0){
                counter++;
            }
        }
        return counter;
    }