微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么重新分配会给我带来不一致的行为?

如何解决为什么重新分配会给我带来不一致的行为?

我目前正在学校上程序编程课程。我们正在使用C99标准的C语言。我与我的老师讨论了此事,但​​我不明白为什么realloc()在他的机器上工作,但对我的机器却不工作。

该程序的目标是解析文本文件students.txt,该文件的学生姓名和其GPA格式如下:

Mary 4.0
Jack 2.45
John 3.9
Jane 3.8
Mike 3.125

我有一个函数可以调整动态分配的数组的大小,当我在CLion IDE中使用重新分配调试器时,它给了我SIGABRT。

我尝试使用在线编译器,但得到realloc(): invalid next size

我整个周末都在尝试调试,但找不到答案,需要帮助。

我的代码当前看起来像这样

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

#define INITIAL_SIZE 4
#define BUFFER_SIZE 512
#define GRADE_CUTOFF 3.9

// ERROR CODES
#define FILE_OPEN_ERROR 1
#define MEMORY_ALLOCATION_ERROR 2

struct student {
    double gpa;
    char *name;
};

struct student *resizeAllocationIfNeeded(struct student *listofStudents,unsigned int studentCount,size_t *currentSize) {

    if (studentCount <= *currentSize) {
        return listofStudents;
    }

    *currentSize *= 2;
    struct student *resizedList = (struct student *) realloc(listofStudents,*currentSize * sizeof(struct student));
    if (resizedList == NULL) {
        perror("Failed to allocate memory");
        exit(MEMORY_ALLOCATION_ERROR);
    }
    return resizedList;
}

size_t getNamesAndGrades(FILE *file,struct student *listofStudents,size_t size) {
    unsigned int studentCount = 0;
    char buffer[BUFFER_SIZE];

    while(fscanf(file,"%s %lf",buffer,&listofStudents[studentCount].gpa) > 0) {
        listofStudents[studentCount].name = strdup(buffer);
        studentCount++;
        listofStudents = resizeAllocationIfNeeded(listofStudents,studentCount,&size);
    }

    return studentCount;
}

void swapStudents(struct student *listofStudents,int x,int y) {
    struct student temp = listofStudents[x];
    listofStudents[x] = listofStudents[y];
    listofStudents[y] = temp;
}

void sortStudentsByGPA(struct student *listofStudents,unsigned int studentCount) {
    for (int i = 0; i < studentCount; i++) {
        for (int j = 0; j < studentCount - i - 1; j++) {
            if (listofStudents[j].gpa < listofStudents[j + 1].gpa) {
                swapStudents(listofStudents,j,j + 1);
            }
        }
    }
}

void printStudentAndGPA(struct student *listofStudents,unsigned int studentCount) {
    for (int i = 0; i < studentCount; i++) {
        if (listofStudents[i].gpa > GRADE_CUTOFF) {
            printf("%s %lf\n",listofStudents[i].name,listofStudents[i].gpa);
        }
        free(listofStudents[i].name);
    }
}

void topStudents(char *fileName) {
    FILE *file = fopen(fileName,"r");

    if (!file) {
        perror("Could not open file for reading");
        exit(FILE_OPEN_ERROR);
    }

    struct student *listofStudents = (struct student *) malloc(INITIAL_SIZE * sizeof(struct student));

    if (listofStudents == NULL) {
        perror("Failed to allocate memory");
        exit(MEMORY_ALLOCATION_ERROR);
    }

    unsigned int studentCount = getNamesAndGrades(file,listofStudents,INITIAL_SIZE);
    sortStudentsByGPA(listofStudents,studentCount);
    printStudentAndGPA(listofStudents,studentCount);
    free(listofStudents);
}

int main() {
    topStudents("students.txt");
    return 0;
}

解决方法

在检查是否需要调整数组大小时,您会遇到篱笆墙错误。

您的初始分配大小为4,这意味着最高有效索引为3

getNamesAndGrades()中的循环中,读入listOfStudents[3]后,您可以将studentCount递增到4。然后,您致电resizeAllocationIfNeeded(listOfStudents,studentCount,&size);

resizeAllocationIfNeeded()studentCount == 4*currentSize == 4内部。所以测试

    if (studentCount <= *currentSize) {
        return listOfStudents;
    }

成功,您不打realloc()就返回。

然后,循环的下一次迭代将分配给listOfStudents[4],这将导致缓冲区溢出。

您需要将该条件更改为studentCount < *currentSize

,

您的代码中有两个错误:一个只是错字,另一个是更严重的逻辑错误。

首先,由于resizeAllocationIfNeeded()中的情况,您分配得太晚了。当studentCount == currentSize时,它不会调整大小(即使应该调整大小),这会使您溢出学生数组并导致问题。

您可以更改条件以解决此问题:

if (studentCount < *currentSize) {
    return listOfStudents;
}

除上述内容外,您的主要错误位于getNamesAndGrades()中,您在这里重新分配内存并将新的指针分配给局部变量。然后,您可以在topStudents()中使用该变量,就好像它已被更新一样。这当然是行不通的,因为topStudents()传递的初始指针在第一个realloc()之后变得无效,并且当getNamesAndGrades()返回时,内存将不可避免地丢失。

您应该传递指向学生数组的指针,或者更好的方法是使函数为您创建数组。

这是一个解决方案,将getNamesAndGrades重命名为getStudents

struct student *getStudents(FILE *file,unsigned int *studentCount) {
    char buffer[BUFFER_SIZE];
    struct student *listOfStudents;
    size_t size = INITIAL_SIZE;

    *studentCount = 0;
    listOfStudents = malloc(size * sizeof(struct student));
    
    if (listOfStudents == NULL) {
        perror("Failed to allocate memory");
        exit(MEMORY_ALLOCATION_ERROR);
    }

    while(fscanf(file,"%511s %lf",buffer,&listOfStudents[*studentCount].gpa) == 2) {
        listOfStudents[*studentCount].name = strdup(buffer);
        (*studentCount)++;
        listOfStudents = resizeAllocationIfNeeded(listOfStudents,*studentCount,&size);
    }

    return listOfStudents;
}

// ...

void topStudents(char *fileName) {
    FILE *file = fopen(fileName,"r");

    if (!file) {
        perror("Could not open file for reading");
        exit(FILE_OPEN_ERROR);
    }

    unsigned int studentCount;
    struct student *listOfStudents = getStudents(file,&studentCount);

    sortStudentsByGPA(listOfStudents,studentCount);
    printStudentAndGPA(listOfStudents,studentCount);
    free(listOfStudents);
}

int main() {
    topStudents("students.txt");
    return 0;
}

其他说明:

  • 在固定大小的缓冲区(在这种情况下为512字节)上扫描时,使用%511s而不是%s,这是缓冲区溢出等待发生的原因。
  • 您正在扫描两个字段,因此请检查fscanf的返回值是== 2,而不是> 0,例如,您不想初始化一个字段,而不要初始化一个字段。 / li>
  • Don't cast the result of malloc() or realloc()
  • 将来,如果您使用的是Linux,则在堆中发生问题时,使用gcc -g -fsanitize=address进行编译将为您提供详细的错误报告,并告诉您确切的内存分配,释放和使用位置。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。