如何正确使用EOF?

How to properly using EOF?

提问人:Tom_the_cat 提问时间:7/1/2018 最后编辑:alkTom_the_cat 更新时间:7/1/2018 访问量:987

问:

我对EOF有疑问。

首先,我正在编写一个简单的程序,用于复制/打印用户的输入。

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

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

enter image description here

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

#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++;
}
c io eof

评论

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

答:

4赞 Spikatrix 7/1/2018 #1

您忘记了 NUL 终止 .因此,您可以在复制过程中调用 Undefined Behavior。改用以下代码获取输入: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]); 

评论

2赞 Basile Starynkevitch 7/1/2018
不,这是正确答案。默认情况下,本地数组没有正确初始化(因此程序员应该考虑初始化它)
1赞 Basile Starynkevitch 7/1/2018
该语句正确地以 null 结尾的字符串origin[i] = '\0';origin
0赞 Yunnosch 7/1/2018
@BasileStarynkevitch 我需要三次重复才能看到 0 被复制。谢谢。
1赞 Yunnosch 7/1/2018 #2

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

origin[i] = '\0';

读完后。

最后输出到那个标记,而不是所有东西

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

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

评论

0赞 David C. Rankin 7/1/2018
可能还想评论一下有多危险......的输入可能非常糟糕。for(i = 0; (c = getchar()) != EOF; ++i)"Hello Newb<nasty shellcode>"
0赞 ChandraKumar 7/1/2018 #3

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

接下来,您将强制在输出 for 循环中打印数组的所有十个元素,这是糟糕的编码实践。

这个简单的片段可以帮到你

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

使用它从文件 ./youpro < file_name_where_to_fetch_input读取输入

感谢 David C. Rankin 在评论中提到这个错误。

评论

0赞 Yunnosch 7/1/2018
我认为 OP 对他们如何输入的描述是合理的。EOF
0赞 ChandraKumar 7/1/2018
也许,确实如此。我不是来自Windows背景...我的答案更倾向于 Linux,一般来说,当从终端编译时......你不能做这些神奇的组合来进入EOF......所以,应该遵循一个std做法,就像我的回答一样。 我在 Windows 上可能错了。.
1赞 David C. Rankin 7/1/2018
“无法输入'EOF'作为输入”??当然有。 在 Linux 上生成一个手册,并在 windoze 上做同样的事情。但请参阅:CTRL+Z 不会在 Windows 10 中生成 EOFCtrl+dEOFCtrl+z
1赞 David C. Rankin 7/1/2018
关于对 IDE 的猜测,您仍然处于一个瘦弱的分支上(无论如何这并不重要),但良好的第一次努力。请记住,在 StackOverflow 上回答时,您进入了 Teacher 的名册。你要确保你是好人之一,而不是比我们开始上课时更让我们感到困惑的老师之一。保持良好的努力,并始终努力做到彻底和正确。
2赞 chqrlie 7/1/2018
字符类的转换说明符没有尾随符,指定的数字必须比数组的大小小 1。它应该写.此外,如果用户输入空行,这将失败,如果忽略返回值 ,则会导致未定义的行为。sscanf("%9[^\n]", origin);scanf()scanf()
3赞 chqrlie 7/1/2018 #4

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

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

以下是更正后的版本:

#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++;
}