【问题标题】:Is getc() defined after it has returned EOF?getc() 返回 EOF 后是否定义?
【发布时间】:2011-01-20 23:28:33
【问题描述】:

我在 C 练习中使用了getc();,在回顾程序后,我发现了一些奇怪的东西。我假设命令行参数中给出的文件至少包含一个字节。 (它连续两次调用getc(); 而不检查EOF。在空文件上尝试后它仍然工作顺利。我的问题是:getc(); 在文件指针上的行为已经用尽(已达到 EOF 且未倒带)未定义还是会一直继续返回 EOF?

我想我可以将此问题扩展到 C STL 中的所有 I/O 函数,请在您的回答中也澄清这一点。

这是程序的代码。该程序应该从所有 cmets 中剥离 C/C++ 源文件(并且它运行良好)。

#include <stdio.h>

int main(int argc, char *argv[]) {
    int state = 0; // state: 0 = normal, 1 = in string, 2 = in comment, 3 = in block comment
    int ignchar = 0; // number of characters to ignore
    int cur, next; // current character and next one
    FILE *fp; // input file

    if (argc == 1) {
        fprintf(stderr, "Usage: %s file.c\n", argv[0]);
        return 1;
    }

    if ((fp = fopen(argv[1], "r")) == NULL) {
        fprintf(stderr, "Error opening file.\n");
        return 2;
    }

    cur = getc(fp); // initialise cur, assumes that the file contains at least one byte
    while ((next = getc(fp)) != EOF) {
        switch (next) {
            case '/':
                if (!state && cur == '/') {
                    state = 2; // start of comment
                    ignchar = 2; // don't print this nor next char (//)
                } else if (state == 3 && cur == '*') {
                    state = 0; // end of block comment
                    ignchar = 2; // don't print this nor next char (*/)
                }
                break;
            case '*':
                if (!state && cur == '/') {
                    state = 3; // start of block comment
                    ignchar = 2; // don't print this nor next char (/*)
                }
                break;
            case '\n':
                if (state == 2) {
                    state = 0;
                    ignchar = 1; // don't print the current char (cur is still in comment)
                }
                break;
            case '"':
                if (state == 0) {
                    state = 1;
                } else if (state == 1) {
                    state = 0;
                }
        }

        if (state <= 1 && !ignchar) putchar(cur);
        if (ignchar) ignchar--;
        cur = next;
    }

    return 0;
}

【问题讨论】:

  • 抱歉,为了清楚起见,这是 C 还是 C++?它看起来像 C,但我宁愿检查而不仅仅是编辑。
  • 它是 C,我认为它也适用于 C++,但我将删除标记以减少歧义。

标签: c io


【解决方案1】:

Stdio 文件保留一个“eof”标志,该标志在第一次到达文件结尾时设置,并且只能通过调用 clearerr 或执行成功的 fseekrewind 来重置。因此,一旦getc 返回EOF 一次,它将继续返回EOF,即使有新数据可用,除非您使用上述方法之一来清除 eof 标志。

一些不符合标准的实现可能会立即使新数据可用。这种行为是有害的,可能会破坏符合标准的应用程序。

【讨论】:

  • 请注意,传统的 Unix 系统有损坏的行为,一些现代系统可能会“为了兼容性”而复制它。所以我会避免依赖任何一种行为。
【解决方案2】:

如果设置了流上的EOF 标志,getc 应该返回EOF(如果你继续调用getc,它应该继续返回EOF)。

【讨论】:

  • 所以在流上的EOF 标志设置后getc(); 应该总是返回EOF,即使你调用它一千次。如果你倒带,fseek(); 会删除标志,我猜?
【解决方案3】:

从逻辑上讲,我认为它应该永远返回EOF

getc 是根据fgetc 定义的。

getc() 函数应等效于 fgetc() ,除非它 被实现为一个宏,它可以多次评估流,所以 参数永远不应该是带有副作用的表达式。

fgetc 的文档说:

如果设置了流的文件结束指示符,或者如果流 在文件结束时,应设置流的文件结束指示符 fgetc() 将返回 EOF。

并且“在文件末尾”可以通过调用feof来确定。

feof 的文档说:

feof() 函数返回非零当且仅当 为流设置了文件结束指示符。

因此,除非发生清除文件结束指示符的事情,否则它应该会一直返回 EOF

【讨论】:

  • 是否有任何理由使用getc(); 而不是fgetc(); 那么因为getc(); 可能在宏中实现,因此可能进行多次评估。 (除了保存一个字符)。
  • 在大多数历史系统上,getc 是一个在 FILE 内联内部插入的宏,并且仅在缓冲区为空时才进行函数调用。现代标准要求 getc 在从同一个 FILE 读取的多个线程下是线程安全的,因此宏定义已经过时,但原则上现代实现仍然可以使用宏,如果它可以有效地检测到只有一个线程正在运行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-05-08
  • 2015-07-03
  • 1970-01-01
  • 2014-01-12
  • 2017-09-23
  • 1970-01-01
相关资源
最近更新 更多