C语言 由fscanf引起的fscanf分析器错误



我正在用C编写一个函数,该函数应该从文件中输入有关学生及其成绩的信息。然而,在运行程序时,有时测试显示分析器在fscanf/fscanf处出现错误,即使输出与预期一致。

/*structure for grade*/
struct Grade {
char subject[51];
int grade;
};
/*structure for student*/
struct Student {
char name[21], surname[21];
int no_grades;
struct Grade grades[100];
};
int input_students(struct Student *students, int n) {
int i = 0, no, j, first;
char name[21], surname[21], subject[51];
FILE *database = fopen("input.txt", "r");
if (database == NULL) {
printf("Error while opening input.txt");
return 0;
}
while (/*i < n && */fscanf(database, "%20s %20s %50s %dn", name, surname, subject,
&no) == 4) {
first = 1;
for (j = 0; j < i; j++) {
if (strcmp(students[j].name, name) == 0 &&
strcmp(students[j].surname, surname) == 0) {
first = 0;
if (students[j].no_grades >= 100) { /*limit for number of grades per student is 100*/
break;
}
strcpy(students[j].grades[students[j].no_grades].subject,
predmet);
students[j].grades[students[j].no_grades].grade = no;
students[j].no_grades++;
break;
}
}
if (first && i < n) {
strcpy(students[i].name, name);
strcpy(students[i].surname, surname);
students[i].no_grades = 0;
strcpy(students[i].grades[students[i].no_grades].subject, subject);
students[i].grades[students[i].no_grades].grade = no;
students[i].no_grades++;
i++;
}
}
/*while (fscanf(database, "%20s %20s %50s %dn", name, surname, subject,
&no) == 4) {
for (j = 0; j < i; j++) {
if (strcmp(students[j].name, name) == 0 &&
strcmp(students[j].surname, surname) == 0) {
if (students[j].no_grades >= 100) {
break;
}
strcpy(students[j].grades[students[j].no_grades].subject,
subject);
students[j].grades[students[j].no_grades].grade = no;
students[j].no_grades++;
break;
}
}
}*/
fclose(database);
return i;
}

/update,注释将代码更改为只有一个循环,同样的事情发生/返回值是从文件中读取其信息的学生的数量,而i <n。>

分析器消息指出fscanf行有错误。

==3928== Invalid write of size 1
==3928== at 0x37154582C7: _IO_vfscanf (in /lib64/libc-2.12.so)
==3928== by 0x371546465A: __isoc99_fscanf (in /lib64/libc-2.12.so)
==3928== by 0x400AD5: input_students (main.c:22)
==3928== by 0x401394: main (main.c:174)
==3928== Address 0x7feff2e00 expected vs actual:
==3928== Expected: stack array "name" of size 21 in frame 2 back from here
==3928== Actual: stack array "surname" of size 21 in frame 2 back from here
==3928== Actual: is 32 before Expected

我似乎找不出这事的原因。我已经仔细检查了每个字符串以''结束,我已经通过调试器运行了程序,它停在应该停的地方,我没有看到它经过并使用未初始化的值,它所取的值如预期的那样。

我已经翻译了函数和变量的名称,因为它最初不是英文的,因此问题不可能是函数的名称与库函数的名称重叠,但我将在这里更改它。

测试代码。这是自动测试

int i, j, no_students;
struct Student students[10];
FILE* database = fopen("input.txt", "w");
fputs("Pero Peric Osnove_racunarstva 8", database);
fputc(10, database);
fputs("Suljo Suljic Osnove_racunarstva 9", database);
fputc(10, database);
fputs("Pero Peric Inzenjerska_matematika_1 6", database);
fclose(database);
no_students=input_students(students, 10);
printf("%dn", no_students);
for (i=0; i<no_students; i++) {
printf("%s %s ", students[i].name, studenti[i].surname);
for (j=0; j<students[i].no_grades; j++)
printf("%s %d ", students[i].grades[j].subject, students[i].grades[j].grade);
printf("n");
}

该测试的输出。

2
Pero Peric Osnove_racunarstva 8 Inzenjerska_matematika_1 6 
Suljo Suljic Osnove_racunarstva 9

当测试代码在main.c中运行时,它没有显示任何编译错误,也没有调试器显示分段错误。

input.txt的示例,由autotest创建。

Pero Peric Osnove_racunarstva 8
Suljo Suljic Osnove_racunarstva 9
Pero Peric Inzenjerska_matematika_1 6

这将是关于分析器的信息,请记住,我仍然只是一个学生

==17000== exp-sgcheck, a stack and global array overrun detector
==17000== NOTE: This is an Experimental-Class Valgrind Tool
==17000== Copyright (C) 2003-2012, and GNU GPL'd, by OpenWorks Ltd et al.
==17000== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==17000== Command: outputP9YqwH

好,下面是我认为发生的事情:

no_students=input_students(students, 10);

所以你期望有10个学生,但你只有4个。所以简单的原因是我们将文件指针database指向行0(而不是地址0),所以你有这个:

row  name   surname subject                  grade_cnt
0    Pero   Peric   Osnove_racunarstva       8n
1    Suljo  Suljic  Osnove_racunarstva       9n
3    Pero   Peric   Inzenjerska_matematika_1 6<END OF FILE>

注意:在第3行末尾可能是文件的末尾。

第一个循环读取第0,1行…3并尝试读取第4行。没有第4行,所以失败了。但可能发生的是,database是指向第4行时,第一个循环完成跳过EOF和读取超出文件缓冲区的结束。

有两种测试方法:

  1. 第4行后输入空行
  2. while()循环之间关闭和重新打开文件。

跳过文件指针的另一个原因是:

fscanf(database, "%20s %20s %50s %dn", name, surname, subject, &no) == 4)

注意%d之后的n如果n没有了会发生什么?fscanf()正在查找'n'.

尝试在输入文件的末尾添加空行。

或删除'n'。

fscanf(database, "%20s %20s %50s %d", name, surname, subject, &no) == 4)

另一个原因是数字,%20s:

fscanf(database, "%s %s %s %d", name, surname, subject, &no) == 4)

如果我说错了,请在评论中告诉我。

由于fscanf读取name的20个字符。

编辑:现在我确信我是对的:读这篇文章。scanffscanf应谨慎使用,最好避免使用。

解决方案非常简单,不用使用名字、姓氏、主题和no这四个变量。声明一个新变量

要简单得多
struct Student s;

并使用它作为输入。

不仅解决了fscanf()的问题,而且其余的代码更简单,更容易阅读。

至于它起作用的原因,还需要进一步研究。

这是重新访问的代码

int input_students(struct Student *students, int n) {
int i = 0, no, j, first;
char name[21], surname[21], subject[51];
FILE *database = fopen("input.txt", "r");
if (database == NULL) {
printf("Error while opening input.txt");
return 0;
}
while (fscanf(database, "%20s %20s %50s %dn", s.name, s.surname, s.grades[0].subject,
&s.grades[0].grade) == 4) {
first = 1;
for (j = 0; j < i; j++) {
if (strcmp(students[j].name, name) == 0 &&
strcmp(students[j].surname, surname) == 0) {
first = 0;
if (students[j].no_grades >= 100) { /*limit for number of grades per student is 100*/
break;
}
students[j].grades[students[j].no_grades] = s.grades[0];
students[j].no_grades++;
break;
}
}
if (first && i < n) {
students[i] = s;
i++;
}
fclose(database);
return i;
}

最新更新