4

我编写了一个名为Mathtext的程序。该程序通过将某些字符范围转换为 Unicode 范围(例如“数学字母符号”)来生成纯文本斜体、粗体、衬线等,从而提供纯文本“样式”。

它作为逐行解释器工作,就像一个shell,在输入一行后输出翻译的行。这意味着文件可以cat/管道输入以翻译整个文件,以及您可以通过按 ^D 来“退出”“shell”的事实,这可以通过标准输入命中 EOF 来检测。

一切正常。但是,当我按 ^D 并退出时,它会出现段错误。我仍然无法完全理解是什么原因造成的。

编译有-g -O0一点帮助;我现在知道问题是由按下 ^D 时转置中的 strlen 调用引起的。但是,在 ^D 期间永远不应该调用转置,因为 eof 是真的!

程序收到信号 SIGSEGV,分段错误。
__strlen_sse2 () 在 ../sysdeps/x86_64/multiarch/../strlen.S:31
31 ../sysdeps/x86_64/multiarch/../strlen.S:没有这样的文件或目录。
    在 ../sysdeps/x86_64/multiarch/../strlen.S
(gdb) 在哪里
#0 __strlen_sse2 () 在 ../sysdeps/x86_64/multiarch/../strlen.S:31
#1 0x0000000000400b0e 转置 (s=0x0, capsDelta=120263, smallDelta=120257, numDelta=0) at mathtext.c:58
#2 0x0000000000400e2b 在 main (argc=2, argv=0x7ffffffffe4b8) at mathtext.c:92
4

3 回答 3

3

您的程序正在取消引用 NULL,因为fgets在错误或 EOF 时返回 NULL,并且您将其直接传递给 transpose,后者天真地使用结果。

于 2010-08-31T12:30:27.240 回答
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();
}
于 2010-08-31T13:02:47.120 回答
1

feof无法预测未来。在您实际按下 ^D 键之前,它不知道这是文件的结尾,此时您的程序将返回等待输入fgets. 读取文件不会产生错误,因为所有输入在开始时已经存在。在转置函数中检查 NULL。

于 2010-08-31T12:43:11.317 回答