【问题标题】:Is there an error in the example code in chapter 1.9 in the classic book "The C Programming Language"?经典著作《The C Programming Language》1.9章的示例代码是否有错误?
【发布时间】:2026-01-26 20:45:02
【问题描述】:

在 Brian 和 Dennis 的经典 C 语言书籍“The C Programming Language”的第 1.9 章中,有一段关于函数“getline”的代码,该函数用于将输入文本的下一行复制到一个char 类型字符串并检查溢出。我引用下面的代码:

int getline(char line[], int maxline);
int getline(char s[], int limit)
{
    int c,i;
    for (i=0; i<limit-1 && (c=getchar())!=EOF && c!='\n'; ++i) /* ** */
        s[i]=c;
    if (c == '\n') {
        s[i]=c;
        ++i;
    }
    s[i] = '\0';
    return i;
}

问题来了:参数'limit'是行的最大长度,所以数组s[]只能包含从s[0]到s[limit-1]的元素集合。如果变量 c 到 getchar() 的最后一个字符是 '\n' 并且这个字符的索引是 limit-1,那么 'for' 循环中的判断部分会因为 'i==limit-1' 而失败,但不是'c!='\n' (按照从左到右的顺序)。接下来if子句会起作用,因为'c=='\n'',那么s[limit-1]=c,那么++i会将i的值设置为limit。 s[i]='\0' 会溢出,因为 s[limit] 超出了字符串的限制。我的分析对不对?感谢您提供任何有用的答案。

【问题讨论】:

  • "如果变量 c 到 getchar() 的最后一个字符是 '\n' 并且这个字符的索引是 limit-1" - 如果 i 已达到 @,则不会读取该字符987654323@。短路布尔 eval 将在 getchar() 之前中断循环,并进行随后的分配和进一步的比较。
  • 是的,你是对的!我错过了这一点。谢谢。
  • 实际上有两点会发生这种情况。

标签: c overflow kernighan-and-ritchie


【解决方案1】:

你的分析是错误的。如果i == limit-1,由于短路评估,循环中断而不读入c。所以,你永远不会输入if (c == '\n')i 仍然是 limit-1 并且没有溢出。

从概念上讲,你可以这样想循环条件:“如果i低于limit-1,则读取一个字符,如果不是EOF或换行符,则进入循环体。”因此,如果ilimit-1,那么您永远不会阅读。

【讨论】:

    【解决方案2】:

    这段代码中有两个短路的评估点。见下文

    for (i=0; i<limit-1 && (c=getchar())!=EOF && c!='\n'; ++i)
    //        (   A   )    (        B        )   (  C  )
    

    所有都用&amp;&amp; 链分隔。执行此代码时,所有三个都必须为真,否则循环将中断。但是短路评估会发生以下情况:

    • 如果 A 为假,则条件为假, B 和 C 均不计算。
    • 否则,如果 B 为假,则条件为假且不计算 C。
    • 否则,如果 C 为假,则条件为假。

    所以……

    • 如果i&lt;limit-1 为假,既不getchar() 和与EOF 的比较,也不与'\n' 的比较。
    • 否则,如果(c=getchar())!=EOF 为假,则不执行与'\n' 的比较。
    • 否则将执行与'\n' 的比较。

    我希望这是有道理的。

    【讨论】: