You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

K&R实现getline时for循环用i<lim-1而非i<lim的原因

K&R getLine函数循环边界设计解析

原书实现代码如下:

#define MAXLINE 1000
int getLine(char s[], int lim)
{
    int c, i;
    for(i = 0; i < lim - 1 && (c = getchar()) != EOF && c!= '\n'; ++i)
        s[i] = c;
    if(c == '\n'){
        s[i] = c;
        ++i;
    }
    s[i] = '\0';
    return i;
}

循环使用i < lim - 1判断的核心原因

这个边界设计完全是为了适配C语言的字符串规则,同时避免数组越界:
C语言没有内置的字符串长度标识,所有合法字符串必须以'\0'(空终止符)作为结尾标记,这个标记本身不计算在字符串有效长度内,但必须占用1个元素的数组存储空间。传入的参数lim是字符数组s的总长度,数组可访问的合法索引范围是0到lim - 1,任何对s[lim]及之后位置的读写都属于越界的未定义行为,轻则导致字符串输出乱码,重则破坏栈内存造成程序崩溃。

为什么i < lim无法满足需求

如果把循环条件替换为i < lim,无论哪种场景都会触发数组越界,顺着代码执行逻辑就能推导出来:

  • for循环的执行顺序是「判断条件→执行循环体写入字符→i自增」,如果条件为i < lim,当输入长度足够时,循环会在i = lim - 1时仍然满足判断条件,将读入的普通字符写入s[lim - 1],之后执行++i让i的值等于lim,才会在下一次条件判断时退出循环。
  • 循环退出后,代码一定会执行s[i] = '\0'写入空终止符,此时i = lim,写入位置s[lim]已经超出了数组合法索引范围。
  • 就算循环是因为读到换行符提前退出,此时i的值最多为lim - 1,进入换行处理分支后会先执行++i把i变成lim,后续写终止符时依然会访问s[lim]的越界位置。

i < lim - 1的边界是怎么保证安全的

这个边界刚好给后续的收尾写入预留了足够的合法位置,所有场景下的数组写入都不会超出lim - 1的最大合法索引:

  • 如果是因为读入长度达到上限退出循环:此时i = lim - 1,最后读入的字符不是换行(否则会提前因换行条件退出),不会进入换行处理分支,直接执行s[lim - 1] = '\0',刚好落在数组最后一个合法位置。
  • 如果是因为读到EOF或换行符退出循环:此时i一定小于lim - 1,写入换行、执行i自增之后,i的最大值为(lim - 2) + 1 = lim - 1,最后写入空终止符的位置依然是s[lim - 1],完全在合法内存范围内。

内容的提问来源于stack exchange,提问作者DiamondCorp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 15:42:17