K& R上的代码是否破碎?

时间:2013-09-29 14:21:44

标签: c

在K& R的'C编程语言'(第二版)第29页,我读过一个我认为已经破解的程序。由于我是初学者,我希望我错了,虽然我无法解释原因。

以下是代码:

#include <stdio.h>
#define MAXLINE 1000 // Maximum input line size

int get1line(char line[], int maxline);
void copy(char to[], char from[]);

// Print longest input line
int
main()
{
  int len;                // Current line lenght
  int max;                // Maximum lenght seen so far
  char line[MAXLINE];     // Current input line
  char longest[MAXLINE];  // Longest line saved here

  max = 0;
  while ((len = get1line(line, MAXLINE)) > 0)
    if (len > max) {
      max = len;
      copy(longest, line);
    }

  if (max > 0)            // There was a line to read
    printf("Longest string read is: %s", longest);

  return 0;
}

// `get1line()` : save a line from stdin into `s`, return `lenght`
int
get1line(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;
}


// `copy()` : copy `from` into `to`; assuming
// `to` is big enough.
void
copy(char to[], char from[])
{
  int i;

  i = 0;
  while ((to[i] = from[i]) != '\0')
    ++i;
}

我的困惑在于:我们使用的是get1line函数,并假设在for - 循环i的末尾设置为lim -1。然后,以下if - 语句将在i更新lim,导致下一条指令(将NULL字符放在字符串末尾的那条指令)破坏堆栈(因为s[lim]未分配,在这种情况下)。

代码是否已损坏?

2 个答案:

答案 0 :(得分:4)

总结:不可能同时使用i == lim-1c == '\n'退出循环,因此您担心的情况永远不会出现。

详细说明:我们可以重写for循环(同时保留其含义)以使事件顺序清晰。

i = 0;
for (;;) {
    if (i >= lim-1) break;      /* (1) */
    c = getchar();
    if (c == EOF) break;        /* (2) */
    if (c == '\n') break;       /* (3) */
    s[i] = c;
    ++i;
}

在循环退出(1)时,c == '\n'不是这种情况,因为如果是这种情况,则循环将在前一次的(3)处退出。*

在循环退出(2)和(3)时,不能是i == lim-1的情况,因为如果是这种情况,则循环将退出(1)。

*这取决于lim至少为2,因此事实上前一次循环。该程序只调用get1linelim等于MAXLINE,所以情况总是这样。**

**在lim小于2时,通过在循环开始之前将c初始化为'\n'以外的值,可以使函数安全。但是如果您担心这种可能性,那么您可能还需要关注lim INT_MIN的可能性,以便lim-1由于整数溢出而导致未定义的行为。< / p>

答案 1 :(得分:1)

lim == 0代码错误,因为它使用c未初始化并添加\ 0。 lim == 1也是错误的,因为它使用c未初始化。使用lim < 2调用函数不是很有用,但它不应该像这样失败。

如果lim > 1则功能正常

for (i = 0; i < lim -1 && (c = getchar()) != EOF && c != '\n'; ++i)
   s[i] = c;

如果i == lim-1c == EOFc == '\n',则循环退出。

  • 如果第一个条件为真(i == lim-1),则最后一个条件肯定不为真(除非lim < 2,如上所述)。

  • 如果第一个条件为假(i < lim-1),那么即使循环以c == \n退出,我们也知道缓冲区中有空格,因为我们知道{{1} }。