首页 文章

如何正确使用EOF?

提问于
浏览
2

我对EOF有疑问 .

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

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

举个例子,我的O.S是Window,当我按顺序输入(Enter - > cntrl z - > Enter)时,我的EOF正常工作 . 如果我输入“Hello”输入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++;
}

4 回答

  • 4

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

    • 读取循环一直持续到文件末尾:如果输入流超过10个字节,代码将导致缓冲区溢出,将字节存储在 origin 数组的末尾之外 . 这是未定义行为的第一种情况 .

    • 本地数组 origin 未初始化,因此其内容不确定 . 在从 stdin 读取的字节之后,不会将空终止符存储到其中 .

    • copy 函数中,依靠空终止符来停止复制循环,但由于没有存储复制循环,因此在复制了从 stdin 读取的所有字节后,可以访问未初始化的内容 . null终止符测试与 while((to[i] = from[i]) != '\0') 中的赋值相结合 . 访问未初始化的数据具有未定义的行为 . 此外,您继续从 origin 读取,直到找到空终止符,如果最终读取超出数组末尾,则会导致进一步的未定义行为,甚至在写入超出 copied 数组末尾时更是如此 .

    • 最后一个循环输出 copied 数组的所有10个元素 .

    • 即使数组 origin 可能在最后包含空字节,从而防止了 copy 函数中的未定义行为 . 输出循环仍然会输出有趣的字符,因为您不会在空终止符处停止,而是将其打印到 stdout ,并在您在 copied 之后读取未初始化的内容时再次具有未定义的行为 .

    • 另请注意,没有参数的 main 的原型是 int main(void) . 您使用的语法(没有返回类型)在'70 and '80s中很常见,但现在已经过时,不应再使用了 .

    这是一个更正版本:

    #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

    你忘了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]);
    
  • 0

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

    origin[i] = '\0';
    

    读完后 .

    最后输出直到那个标记,而不是一切

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

    这样可以保证阵列足够大以保持输入(包括添加的 '\0' ) . 但是,您应该对此进行保护,例如对任何循环使用双重条件,检查是否允许访问beyound允许的最高数组索引 .

  • 1

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

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

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

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

    用它来读取文件./youpro <file_name_where_to_fetch_input中的输入

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

相关问题