重新分配一个更大的元素 (2x) 时 C 程序崩溃

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

我是 C 语言的初学者,我正在尝试以最简单的方式学习动态内存分配 - 我有一个管理学生记录的程序,但我有一些我一生都无法解决的问题即使在 googleing/Whosebuging 几个小时之后 -- 这是这个程序要完成的:

这是在 GDB 调试器中发现的导致此错误的原因(问题来自 addRecord 函数和 freeMemory 函数):

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

并且当在 GDB 调试器之外使用 GCC 和 运行 编译时,它只会出现段错误。

只有当我在程序中执行这些选项时才会发生这种情况:

1) 添加一条记录

2) 删除记录

3) 添加一条记录(然后崩溃)

1) 添加一条记录

2) 添加一条记录(然后崩溃)

我不确定是什么原因造成的,我也不知道如何解决,谁能帮我指明正确的方向?我尝试在 freeMemory 函数中 realloc 成功后添加一个 free 但无济于事,这会导致程序在删除添加的记录时崩溃。

代码如下:

    #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;
}

错误似乎在您的函数中 addRecord。你有表达式

tempPtr = safe_trim(ln, records+1*sizeof(*ln));

由于 C operator precedence,它给出了 records + (sizeof (*ln)) 的大小。乘法先于加法。该行应更改为:

tempPtr = safe_trim(ln, (records + 1) * sizeof(*ln));

还因为 safe_trim 始终 return 一个有效的指针,无论是原始指针还是调整后的指针,此检查

tempPtr = safe_trim(fn, records+1*sizeof(*fn));
if(tempPtr){
    printf("ALLOCATION successfully");
    fn = tempPtr;
}
else{
    printf("FAILED");
    // Realloc Failed
}

永远不会遵循 else 分支并且不会处理分配失败。将对 safe_trim 的调用替换为 realloc 应该不会对程序产生任何影响,除非允许检测分配失败。

我已经解决了问题。这是因为我没有使用三重指针,它没有寻址原始变量。

#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;
}