如何解决为什么重新分配会给我带来不一致的行为?
我目前正在学校上程序编程课程。我们正在使用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()
orrealloc()
- 将来,如果您使用的是Linux,则在堆中发生问题时,使用
gcc -g -fsanitize=address
进行编译将为您提供详细的错误报告,并告诉您确切的内存分配,释放和使用位置。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。