【问题标题】:Trouble debugging seg fault when using fscanf使用 fscanf 时无法调试 seg 错误
【发布时间】:2013-09-24 01:57:49
【问题描述】:

下面的 fscanf 行出现分段错误。我为每个变量添加了字符宽度以尝试修复它,所以我不明白为什么它会出现段错误。

我从一个 CSV 文件读入一个结构数组。

结构是:

typedef struct Person
    {
    int ID;
    int salary;
    int deleted;
    char salutation[4];
    char firstName[21];
    char surName[31];
    char job[16];
    } Person;

我声明了结构数组:

Person* persons;
persons = (Person*)malloc(SIZE * sizeof(Person));

然后使用这个 while 循环将 CSV 文件读入值:

(i 初始化为 0)

while(fscanf(f, "%d,%3[^,],%20[^,],%30[^,],%15[^,],%d,%d", &inPersons[i].ID, inPersons[i].salutation, inPersons[i].firstName, inPersons[i].surName, inPersons[i].job, &inPersons[i].salary, &inPersons[i].deleted)!=EOF)
            {
            newID = inPersons[i].ID;
            i++;
            }

段错误发生在 fscanf 语句期间。我没有使用 valgrind 的经验,但它给了我该行的这个错误:

==16810== Invalid write of size 4
==16810==    at 0x578215: _IO_vfscanf (in /lib/libc-2.12.so)
==16810==    by 0x585368: __isoc99_fscanf (in /lib/libc-2.12.so)
==16810==    by 0x8048951: loadDb (Database.c:23)
==16810==    by 0x8048711: menu (Menu.c:37)
==16810==    by 0x804861E: main (main.c:6)
==16810==  Address 0x27230128 is not stack'd, malloc'd or (recently) free'd

我也得到了同一行的这些 Valgrind 错误:

==18457== Use of uninitialised value of size 4
==18457==    at 0x405A215: _IO_vfscanf (in /lib/libc-2.12.so)
==18457==    by 0x4067368: __isoc99_fscanf (in /lib/libc-2.12.so)
==18457==    by 0x8048943: loadDb (Database.c:23)
==18457==    by 0x8048711: menu (Menu.c:37)
==18457==    by 0x804861E: main (main.c:6)


==18457== Process terminating with default action of signal 11 (SIGSEGV)
==18457==  Access not within mapped region at address 0x5C5E4128
==18457==    at 0x405A215: _IO_vfscanf (in /lib/libc-2.12.so)
==18457==    by 0x4067368: __isoc99_fscanf (in /lib/libc-2.12.so)
==18457==    by 0x8048943: loadDb (Database.c:23)
==18457==    by 0x8048711: menu (Menu.c:37)
==18457==    by 0x804861E: main (main.c:6)

【问题讨论】:

  • 另外:此代码在 Windows 中运行良好,但在 Linux 中崩溃

标签: c segmentation-fault valgrind scanf


【解决方案1】:

如果你有一个字母或标点字符,而你应该有一个数字,你就会有一个无限循环,并且你会溢出你分配的人员数组的边界。你应该检查:

while (i < SIZE && (num = fscanf(f, "...", ...)) == 7)
{
    ...process valid input...
}
...consider what to do here, using `num` to distinguish between EOF and failed conversions...

valgrind 无法识别内存的事实令人费解。您已经采取了大部分相关步骤来确保不会出现缓冲区溢出(除了正确检查fscanf() 的状态之外)。

哦……你分配persons;你正在阅读inPersons...这是正确的?

我想我会编写一个函数来调用fscanf() 并检测和报告错误,然后从while 循环中调用它:

while (i < SIZE && get_person(&persons[i]) != EOF)
   ...

或:

while (i < SIZE && get_person(&inPersons[i]) != EOF)
   ...

这还允许您从fscanf() 切换到fgets()sscanf(),或getline()sscanf()

【讨论】:

  • 谢谢,我会做出这些改变,但是在相同输入的 Windows 中没有出现 seg 错误,但在 Linux 中出现了。我在 OP 中为该行添加了 2 个来自 Valgrind 的错误。
  • persons 被传递给具有参数的读取函数 (Person *inPersons)
  • 你在某个地方遇到了一些严重的问题。你已经成功地把事情搞砸了。是时候忽略它在 Windows 上工作并在 SSCCE (Short, Self-Contained, Correct Example) 上启动的事实了。使用您现有的程序,然后开始删除代码。删除valgrind报告的站点后的代码。然后尝试从网站报告之前删除代码。确保在删除代码后得到相同的错误(尽管地址不同)。当您无法在不丢失错误的情况下删除任何内容时,您将获得 SSCCE 的最佳机会。贴出代码。
  • 好的,我已经完成了,我将发布一个新问题,否则它会变得太乱。
  • 好的。另一个建议:确保您使用 GCC set fussy 进行编译,并且没有编译警告。我经常使用:gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition,如果不干净,代码就不会运行。 (在修复警告之前,我不会运行代码。)如果您愿意,可以选择-std=c99(甚至-std=c89)。但是,如果有警告,很可能其中之一是您遇到的错误的基础。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-28
  • 1970-01-01
  • 2017-01-25
  • 1970-01-01
相关资源
最近更新 更多