c - 由 fscanf 引起的 fscanf 分析器错误

标签 c scanf profiler

我正在用 C 语言编写一个函数,该函数应该从文件中输入有关学生及其成绩的信息。然而,在运行程序时,测试有时会在 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 %d\n", 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 %d\n", 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;
}

/更新,注释将代码更改为仅一个循环,同样的事情发生/ 返回值是从文件中读取信息的学生数量,而i

分析器消息指示 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

我似乎无法找到这是什么原因。我仔细检查了每个字符串是否以“\0”结尾,我已通过调试器运行该程序,它在应该的位置停止,我没有看到它遍历并使用未初始化的值,并且它采用的值符合预期.

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

测试代码。这是一个自动测试。

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("%d\n", 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 示例。

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       8\n
1    Suljo  Suljic  Osnove_racunarstva       9\n
3    Pero   Peric   Inzenjerska_matematika_1 6<END OF FILE>

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

因此,您首先循环读取第 0、1 ... 3 行,然后尝试读取第 4 行。没有第 4 行,因此失败。但可能发生的情况是,当第一个循环完成时,数据库指向第 4 行,跳过 EOF 并读取超出文件缓冲区末尾的内容。

有两种测试方法:

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

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

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

注意 \n%d 之后,如果 \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 个字符。

编辑:现在我确信我是对的: 阅读 thisscanffscanf 应谨慎使用,最好的情况下应避免使用。

关于c - 由 fscanf 引起的 fscanf 分析器错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66188754/

相关文章:

c - C中的输入切换

c - 使用 fscanf 和 fprintf 更新文件

c - scanf 上的段错误并非在所有系统上都存在

Symfony/PHPUnit 在测试中启用分析器

c - 使用 C 解析 MIDI 文件 -(分配内存时可能出现问题)

c - 我没有得到 C 程序所需的输出

sql - Sql CE 的探查器

visual-studio-2012 - Visual Studio 2012 探查器不会产生任何输出 (Windows 8)

c++ - 在 C 中写入 C++ 指针

c - Libelf 会创建损坏的输出文件,即使什么都不做