为什么这个程序是segfaulting?

时间:2010-08-31 12:20:45

标签: c shell eof

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

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

一切正常。但是,当我按^ D并退出时,它会发生段错误。我仍然无法理解造成这种情况的原因。

使用-g -O0进行编译会有所帮助;我现在知道问题来自于按下^ D时转置中的strlen调用。但是,在^ D期间永远不应该调用转置,因为eof是真的!

Program received signal SIGSEGV, Segmentation fault.
__strlen_sse2 () at ../sysdeps/x86_64/multiarch/../strlen.S:31
31    ../sysdeps/x86_64/multiarch/../strlen.S: No such file or directory.
    in ../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

3 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分: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();
}

答案 2 :(得分:1)

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