【问题标题】:How to properly using EOF?如何正确使用EOF?
【发布时间】:2018-07-01 05:26:49
【问题描述】:

我对 EOF 有疑​​问。

首先,我正在编写一个简单的程序来处理/打印用户的输入。

但是,程序也会在输出中复制 EOF。

例如,我的操作系统是窗口,当我按顺序键入 (Enter -> cntrl + z -> Enter) 时,我的 EOF 工作。如果我输入“Hello” + Enter + EOF 组合键,输出会在复制的用户输入的末尾打印出奇怪的字母('?')。

我怎样才能摆脱'?在输出的末尾,为什么会这样?

#include <stdio.h>

void copy(char to[], char from[]);

main()
{
    int i;
    int c;

    char origin[10];
    char copied[10];

    for(i = 0; (c = getchar()) != EOF; ++i)
    {
        origin[i] = c;
    }

    copy(copied, origin);


    for(i = 0; i < 10; i++)
        putchar(copied[i]); 



}

void copy(char to[], char from[])
{
    int i;

    i = 0;
    while((to[i] = from[i]) != '\0')
        i++;
}

【问题讨论】:

  • 不是问题,但您应该使用标准的int main(void) 而不是main()
  • EOF notchar 类型的值(例如,在char-s 为unsigned 的计算机上,EOF 可能为-1) .所以你不能复制EOF,根据定义!
  • 一个好问题。只使用屏幕截图很可惜。请将文本粘贴为文本。要从 CMD 窗口中的窗口执行操作,请在窗口的属性选项 TAB 中启用“快速编辑模式”。

标签: c io eof


【解决方案1】:

您忘记了 NUL 终止 origin。因此,您在复制期间调用未定义行为。请改用以下代码获取输入:

for(i = 0; i < 9 && (c = getchar()) != EOF; ++i) /* `i < 9` to prevent array overruns */
{
    origin[i] = c;
}
origin[i] = '\0'; /* NUL-terminate your string */

同时将打印代码更改为:

for(i = 0; copied[i] != '\0'; i++) /* Print until a NUL-terminator */
    putchar(copied[i]); 

【讨论】:

  • 不,这是正确的答案。本地数组默认没有正确初始化(所以程序员应该考虑初始化它)
  • origin[i] = '\0'; 语句正确地以空值终止 origin 中的字符串
  • @BasileStarynkevitch 我需要三个重新分析才能看到 0 被复制。谢谢。
【解决方案2】:

该问题与EOF 完全无关,您的代码中有多个问题导致潜在的未定义行为和不必要的副作用:

  • 读取循环一直持续到文件末尾:如果输入流长于 10 个字节,代码将导致缓冲区溢出,存储超出 origin 数组末尾的字节。这是未定义行为的第一种情况。
  • 本地数组origin 未初始化,因此其内容不确定。在从stdin 读取的字节之后,您不会将空终止符存储到其中。
  • copy 函数中,您依靠空终止符来停止复制循环,但由于没有存储在那里,您在复制从stdin 读取的所有字节后访问未初始化的内容。空终止符测试与while((to[i] = from[i]) != '\0') 中的赋值相结合。访问未初始化的数据具有未定义的行为。此外,您会一直从origin 读取,直到找到空终止符,如果您最终读取超出数组末尾,则会导致进一步的未定义行为,当写入超出copied 数组末尾时更是如此。李>
  • 最后的循环输出copied数组的所有10个元素。
  • 即使数组origin 可能偶然在末尾包含空字节,从而防止copy 函数中的未定义行为。输出循环仍会输出有趣的字符,因为您不会在空终止符处停止,而是将其打印到 stdout,然后当您在 copied 的末尾读取未初始化的内容时再次出现未定义的行为。
  • 另请注意,不带参数的main 的原型是int main(void)。您使用的没有返回类型的语法在 70 和 80 年代很常见,但现在已过时,不应再使用。

这是一个更正的版本:

#include <stdio.h>

void copy(char to[], char from[]);

int main(void) {
    int i;
    int c;
    char origin[10];
    char copied[10];

    for (i = 0; i < 10 - 1 && (c = getchar()) != EOF; i++) {
        origin[i] = c;
    }
    origin[i] = '\0';

    copy(copied, origin);

    for (i = 0; copied[i] != '\0'; i++) {
        putchar(copied[i]);
    }

    return 0;
}

void copy(char to[], char from[]) {
    int i;

    i = 0;
    while ((to[i] = from[i]) != '\0')
        i++;
}

【讨论】:

    【解决方案3】:

    您正在无条件地输出数组的所有 10 个成员。
    您可以通过在输出的字母末尾附加常用的'\0' 来修复。
    用一个

    origin[i] = '\0';
    

    读完之后。

    最后输出到那个标记,而不是全部

    for(i = 0; copied[i]!='\0'; i++)
    

    这使您假设数组足够大以保留输入(包括添加的'\0')。但是,您应该防止这种情况发生,例如对任何循环使用双重条件,检查是否访问超出允许的最高数组索引。

    【讨论】:

    • 可能还想评论for(i = 0; (c = getchar()) != EOF; ++i) 的危险性..."Hello Newb&lt;nasty shellcode&gt;" 的输入可能非常糟糕。
    【解决方案4】:

    您正在使用一个 IDE(可能是 CodeBlocks),它在后续 IO 操作之间使用页面缓冲区,这就是您实际获得输出的原因。

    接下来,您将强制在输出 for 循环中打印数组的所有十个元素,这是不好的编码习惯。

    这个简单的 sn-p 可以帮助你

    scanf("%10[^\n]s",input);
    

    使用它从文件 ./youpro

    感谢 David C. Rankin 在评论中提及错误。

    【讨论】:

    • 我认为 OP 对他们如何输入 EOF 的描述是合理的。
    • 也许吧。我不是windows背景...而且我的答案更倾向于Linux,通常在从终端编译时...你不能做这些神奇的组合来输入EOF...所以,应该遵循一个标准的做法,所以作为我的回答。我可能在 Windows 上错了..
    • "没有办法输入'EOF'作为输入"??当然有。 Linux 上的Ctrl+d 生成手册EOFCtrl+z 在windoze 上执行相同操作。但请参阅:CTRL+Z does not generate EOF in Windows 10
    • 关于 IDE 的猜测,您仍然处于一个狭窄的分支上(无论如何这并不是真正相关的),但良好的第一次努力。请记住,在 StackOverflow 上回答时,您会进入教师名单。您要确保自己是优秀的老师之一,而不是让我们比我们开始上课时更加困惑的老师之一。继续努力,始终做到彻底和正确。
    • 字符类的转换说明符有尾随s,并且指定的数字必须比数组的大小小一。应该写成scanf("%9[^\n]", origin);。此外,如果用户输入一个空行,这个scanf() 将失败,如果scanf() 的返回值被忽略,则会导致未定义的行为。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-25
    • 2018-01-01
    • 1970-01-01
    • 2020-12-04
    • 2012-07-12
    • 2013-01-21
    • 2021-10-24
    相关资源
    最近更新 更多