为什么这个程序会出现段错误?

4
我已经编写了一个程序,名为Mathtext。该程序通过将某些字符范围转换为Unicode范围(例如“数学字母符号”)来产生纯文本斜体、粗体、衬线等样式的纯文本。
它作为逐行解释器工作,类似于shell,在输入一行后输出翻译后的行。这意味着可以通过cat/piped来翻译整个文件,同时您也可以通过按下^D来“退出”“shell”,stdin检测到EOF时会退出。
一切都很正常。然而,当我按下^D并退出时,它会发生段错误。我仍然无法完全理解是什么原因导致了这个问题。
使用-g -O0编译有所帮助;现在我知道问题出现在transpose中的strlen调用中,当按下^D时会出现。然而,在^D期间永远不应该调用transpose,因为eof为真!
程序收到 SIGSEGV 信号,出现段错误。 __strlen_sse2 () 位于 ../sysdeps/x86_64/multiarch/../strlen.S 的第 31 行。 ../sysdeps/x86_64/multiarch/../strlen.S:没有那个文件或目录。 在 ../sysdeps/x86_64/multiarch/../strlen.S 中。 (gdb) where #0 __strlen_sse2 () at ../sysdeps/x86_64/multiarch/../strlen.S:31 #1 0x0000000000400b0e in transpose (s=0x0, capsDelta=120263, smallDelta=120257, numDelta=0) at mathtext.c:58 #2 0x0000000000400e2b in main (argc=2, argv=0x7fffffffe4b8) at mathtext.c:92

请展示mathtext.c文件中第92行左右的代码。 - Fosco
3个回答

3

feof()的大多数用法都是错误的 - 这个程序在主循环中完美地演示了这一点:

char temp[1048576];
do {
    if (!strcmp(argv[1], "serifb"))
        transpose(fgets(temp, 1048576, stdin), 119808 - 'A', 119834 - 'a', 120782 - '0');
    else if (!strcmp(argv[1], "serifi"))
        transpose(fgets(temp, 1048576, stdin), 119860 - 'A', 119886 - 'a', 0);
    else if (!strcmp(argv[1], "serifbi"))
        transpose(fgets(temp, 1048576, stdin), 119912 - 'A', 119938 - 'a', 0);
    else if (!strcmp(argv[1], "sans"))
        transpose(fgets(temp, 1048576, stdin), 120224 - 'A', 120250 - 'a', 120802 - '0');
    else if (!strcmp(argv[1], "sansb"))
        transpose(fgets(temp, 1048576, stdin), 120276 - 'A', 120302 - 'a', 120812 - '0');
    else if (!strcmp(argv[1], "sansi"))
        transpose(fgets(temp, 1048576, stdin), 120328 - 'A', 120354 - 'a', 0);
    else if (!strcmp(argv[1], "sansbi"))
        transpose(fgets(temp, 1048576, stdin), 120380 - 'A', 120406 - 'a', 0);
    else if (!strcmp(argv[1], "mono"))
        transpose(fgets(temp, 1048576, stdin), 120432 - 'A', 120458 - 'a', 120822 - '0');
    else if (!strcmp(argv[1], "fullwidth"))
        transposeBlock(fgets(temp, 1048576, stdin), '!', '~', 65281 - '!');
    else return help();
} while(!feof(stdin));

在文件末尾,fgets() 将返回 NULL,然后下一次调用 feof() 将返回 true。因此,正确的方法是测试输入函数的返回值 - 并且既然您已经进行了该测试,就没有必要调用 feof()(除非您想区分文件错误和文件结束)。
char temp[1048576];
while (fgets(temp, sizeof temp, stdin) != NULL) {
    if (!strcmp(argv[1], "serifb"))
        transpose(temp, 119808 - 'A', 119834 - 'a', 120782 - '0');
    else if (!strcmp(argv[1], "serifi"))
        transpose(temp, 119860 - 'A', 119886 - 'a', 0);
    else if (!strcmp(argv[1], "serifbi"))
        transpose(temp, 119912 - 'A', 119938 - 'a', 0);
    else if (!strcmp(argv[1], "sans"))
        transpose(temp, 120224 - 'A', 120250 - 'a', 120802 - '0');
    else if (!strcmp(argv[1], "sansb"))
        transpose(temp, 120276 - 'A', 120302 - 'a', 120812 - '0');
    else if (!strcmp(argv[1], "sansi"))
        transpose(temp, 120328 - 'A', 120354 - 'a', 0);
    else if (!strcmp(argv[1], "sansbi"))
        transpose(temp, 120380 - 'A', 120406 - 'a', 0);
    else if (!strcmp(argv[1], "mono"))
        transpose(temp, 120432 - 'A', 120458 - 'a', 120822 - '0');
    else if (!strcmp(argv[1], "fullwidth"))
        transposeBlock(temp, '!', '~', 65281 - '!');
    else return help();
}

3

您的程序在解除引用 NULL,因为 fgets 在错误或 EOF 时返回 NULL,并且您直接将其传递给 naively 使用结果的 transpose。


1

feof 无法预测未来。它不知道文件结束,直到你实际按下^D键,此时你的程序已经回到等待输入的状态中fgets。读取文件不会产生错误,因为所有输入都已经在开头了。在转置函数中检查NULL。


更简单的说法是:只有在读取超过EOF时,才会设置EOF。最后一个有效读取将读取到但不超过EOF,因此失败的读取将恰好有0个字节可读取。 - Martin York

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接