The C Programming Language 示例 1.9 的难点

Difficulties with an example 1.9 of The C Programming Language

我正在完成 C 程序设计语言 第一章的练习,虽然我理解大部分所说和显示的内容,但有一个例子我不明白。

在 1.9 中,有一个函数显示 return 行的长度,同时将作为参数传递的 char 数组设置为内容。

int get_line(char s[], int lim)
{
    int c, i, l;

    for (i = 0, l = 0; (c = getchar()) != EOF && c != '\n'; ++i) {
        if (i < lim - 1)
            s[l++] = c;
    }
    if (c == '\n')
        if (l < lim - 1)
            s[l++] = c;
    s[l] = '[=10=]';

    return l;
}

我不明白的是为什么我们需要这个:if (c == '\n') {...}。这不能在for循环中组合吗?我们在哪里明确检查 c 不等于 '\n'?我无法理解为什么这需要是一个外部条件。

任何灯罩都会有所帮助! 谢谢!

如果 c 等于 EOFc 等于 '\n',则退出 for 循环。因此,在 for 循环之后,如果想知道 c 有哪个值,则必须测试

如果你想把它放在循环中,你必须这样做:

int get_line(char s[], int lim)
{
    int c, i, l;

    for (i = 0, l = 0; (c = getchar()) != EOF; ++i) {
        if ((i < lim - 1) && (c != '\n'))
            s[l++] = c;
        else if (c == '\n') {
            if (l < lim - 1)
                s[l++] = c;
             break;
         }
    }

    s[l] = '[=10=]';

    return l;
}

因此,如您所见,将条件包装在循环内,导致更多条件检查和 break 语句。

is why we need this: if (c == '\n') {...}.

get_line() 在结构上是:

get_line() {
  initialize

  while get, A not true and B not true
    perform X

  if B
    perform X
  
  finalize

循环在两种情况下退出。对于其中之一 (c == '\n'),我们仍然希望在某处执行 X ,因为这是函数目标的一部分。


​Could this not be combined in the for-loop?

它可以合并,但我们有 2 个位置退出循环。

典型的编码准则提倡在单个位置退出循环。如果我们搁置那个目标,那么:

get_line() {
  initialize

  while get, A not true
    perform X
    if B quit the loop
  
  finalize

如下所示,使用 相同数量的 条件检查,但有 2 个循环出口点。

int get_line(char s[], int lim) {
    int c, i, l;

    for (i = 0, l = 0; (c = getchar()) != EOF; ++i) {
        if (i < lim - 1)
            s[l++] = c;
        if (c == '\n')
            break;
    }

    s[l] = '[=12=]';
    return l;
}

我们可以扭曲代码,让 2 个检查回到同一行,而不是在循环后 if (c == '\n')。从风格上讲,这可能更难理解。

int get_line(char s[], int lim) {
    int c, i, l;

    for (i = 0, l = 0, c = 0; c != '\n' && (c = getchar()) != EOF; ++i) {
        if (i < lim - 1)
            s[l++] = c;
    }
    s[l] = '[=13=]';

    return l;
}

最后,代码可以使用改进:

  • 不需要 il 索引计数器。一个就够了。

  • 数组大小和索引最好使用 size_t 类型。警告:size_t 是一些 unsigned 类型。

  • 使用前导大小参数可以更好地进行静态代码分析和自文档化代码:lims[].

    相关
  • 避免对输入参数进行数学计算以免导致溢出。我们对本地对象有更多的范围控制。

  • lim 处于极值或零时要小心。

  • 而不是在声明后赋值,在可行的情况下进行初始化。例如。 int i = 0;


get_line() {
  initialize

  while B not true, get, A not true
    perform X
  
  finalize

#include <stdio.h>
#include <stdlib.h>
      
size_t get_line(size_t size, char s[size]) {
  int ch = 0;
  size_t i = 0;

  while (ch != '\n' && (ch = getchar()) != EOF) {
    if (i + 1 < size)
      s[i++] = (char) ch;
  }

  // size might have been pathologically 0, so no room for [=15=]
  if (i < size) {
    s[i] = '[=15=]';
  }
  return i;
}