【问题标题】:Is the behavior of getchar() correct for this program?该程序的 getchar() 行为是否正确?
【发布时间】:2012-02-07 14:46:29
【问题描述】:

以下代码显示出奇怪的行为。如果我按换行符给出输入,那么只有它打印直方图值,否则如果我直接输入 EOF(^Z),它会显示全零。 getchar() 函数是否存在问题,它仅在按下换行符时才接受输入。

#include <stdio.h>
#define IN 1 /* inside a word */
#define OUT 0 /* outside a word */
#define MAXLEN 50
/* count lines, words, and characters in input */
main()
{
    int c, i, j, nc, state;
    int wordlength[MAXLEN];
    state = OUT;
    nc = 0;
    for (i = 0; i < MAXLEN; i++)
        wordlength[i] = 0;
    while ((c = getchar()) != EOF) {
        if (c == ' ' || c == '\n' || c == '\t') {
            if (state == IN) {
                wordlength[nc-1]++;
            }
            state = OUT;

        }
        else if (state == OUT) {
            //putchar('\n');
            state = IN;
            nc = 0;
        }
        if (state == IN)    {
            ++nc;
        }
    }

    for (j = 0; j < MAXLEN; j++)
            printf("\n%d - %d",j,wordlength[j]);

    for (i = 10; i >= 0; i--) {
        for (j = 0; j < MAXLEN; j++)
            printf(((wordlength[j] > i)?"|":" "));
        printf("\n");

    }

}

【问题讨论】:

  • 请学习按照 C99(旧 C 标准;当前标准是 C2011)的要求编写 C,并为 main() 函数提供显式返回类型。当你忽略参数列表时,我强烈推荐int main(void),当你处理命令行参数时,我强烈推荐int main(int argc, char **argv)
  • 不幸的是,标准输入通常是行缓冲的,您必须按 Enter 键才能输入任何键盘输入。就是这样。你可以让它无缓冲或让它不这样做。
  • @Jonathan 感谢您的建议。我正在关注一本书并尝试编写解决方案,这些解决方案仅使用到阅读点的信息。
  • @Jeff 你能告诉我怎么做吗?
  • 我不清楚你在问什么......

标签: c getchar


【解决方案1】:

除非我在我的机器上指示 EOF(Control-D ; 如果您使用 Control-Z,则表明您在 Windows 上运行)。如果您指示 EOF 没有最后的空格,则不会将最后一个单词添加到直方图中。当然,您还应该检查字长是否过大,以免在wordlength 数组之外进行索引(if (nc &gt; MAXSIZE) nc = MAXSIZE; 将所有非常长的字视为相同大小)。

在主处理循环之后,您应该检查是否有nc &gt; 0,如果是,则增加wordlength 中的相应条目。

也考虑使用&lt;ctype.h&gt; 中的isspace()

我尽可能使用enum 而不是#define,以便在调试器中可以使用这些符号。你小心地避免了一个常见的错误;您将变量 c 设置为 int,而不是 char

#include <stdio.h>

enum { IN =  1, OUT = 0 };  /* inside, outside a word */
enum { MAXLEN = 50 };

/* count lines, words, and characters in input */
int main(void)
{
    int c, i, j, nc, state;
    int wordlength[MAXLEN];
    state = OUT;
    nc = 0;

    for (i = 0; i < MAXLEN; i++)
        wordlength[i] = 0;

    while ((c = getchar()) != EOF) 
    {
        if (c == ' ' || c == '\n' || c == '\t') 
        {
            if (state == IN) 
            {
                if (nc > MAXLEN)
                    nc = MAXLEN;    /* All long words grouped together */
                wordlength[nc-1]++;
            }
            state = OUT;
        }
        else if (state == OUT) 
        {
            state = IN;
            nc = 0;
        }
        if (state == IN)
            ++nc;
    }

    if (nc > 0)
    {
        if (nc > MAXLEN)
            nc = MAXLEN;    /* All long words grouped together */
        wordlength[nc-1]++;
    }

    for (j = 0; j < MAXLEN; j++)
        printf("\n%d - %d", j, wordlength[j]);

    for (i = 10; i >= 0; i--) 
    {
        for (j = 0; j < MAXLEN; j++)
            putchar( (wordlength[j] > i) ? '|' : ' ');
        printf("\n");
    }
    return 0;
}

您说您的机器有问题。对于声称在系统中发现错误,我会非常谨慎,尤其是在getchar() 这样明显的调用中。我不能排除这种可能性,但这是我最不想指责的事情。在认为getchar() 中存在错误之前,我会花很多时间找出我做错了什么来破坏事物。


在 cmets 中,您要求被告知为什么您的程序无法在您的环境中运行。由于您尚未(尚未)正式确定运行程序的平台/环境,因此这是不可能的。

但是,我已经证明,您发布的原始程序在类 Unix 环境中可以正常运行(我正在 MacOS X 10.7.2 上进行测试,但对于任何其他类似的类 Unix 系统都可以正常运行) )。修改后的版本效果稍好一些;即使后面没有空格或换行符,它也会计算最后输入的单词。

如推断,如果您在 Windows 上工作,那么终端 I/O 模型可能会有所不同。特别是,C 标准要求文本文件(可能包括终端输入)必须在 EOF 之前以换行符结尾;最后一个换行符之后的任何字符都可能被丢弃,但这取决于平台。二进制文件的行为是不同的。如果最后一个换行符之后的数据,那将与您报告的行为一致。这很可能是预期的行为 - 如果您查看未识别系统的文档。这是 P J Plauger 在其出色的(但有些过时的)“标准 C 库”中确定的实现之间的差异之一。

但是,如果我的假设是正确的,那么我仍然希望明确说明您的代码是正确的(足够了);问题在于您的期望与记录在案的系统行为不符。请注意,报告您正在使用的平台有时至关重要。当您侵犯边缘情况时,它往往更加重要。而且您仍然极不可能遇到getchar() 中的错误。

顺便说一句,当我在测试时,我需要输入两次 Control-D(这正是我所期望的)。第一次将我在 (abc) 行中输入的字符作为 3 字节读取刷新到程序中;第二个也将我输入的字符(全部为零)作为 0 字节读取刷新到程序中,然后 getchar() 将其解释为 EOF。我还测试了abc(最后是空白),然后是 EOF。您的代码没有计算 abc 没有空格; abc 后面跟一个空格时,它确实计算在内。

【讨论】:

  • 您的回答描述了该程序的预期行为。我一直小心不要输入任何单词> MAXLEN。感谢您提出诸如枚举和其他条件之类的替代方案;但是编写这个程序的全部目的是学习 C。如果你能告诉我为什么这个程序\m 不工作而不是建议替代方法来完成这项工作,我将更加感激。
猜你喜欢
  • 2014-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-17
  • 1970-01-01
  • 2014-08-07
相关资源
最近更新 更多