在嵌套的 while 循环中重新测试 while 循环条件

Retesting while loop condition inside a nested while loop

在这个程序中我不太明白为什么我们在嵌套的while循环中测试第一个while循环的条件1

程序是说一个字符串有多少个单词,单词之间用空格隔开

代码如下:

int count_words(char *ch){
    int c = 0;
    while(*ch!='[=10=]'){
        if(*ch==' '){
            ch++;
            continue;
        }
        c++;
        while(*ch && *ch !=' '){
            ch++;
        }
    }
    return c;
}

我不明白的是,为什么第一个 while 循环中的条件 1:(*ch!='\0') 在第二个 while 循环中再次被测试。

我是否总是需要在嵌套的 while 循环中测试返回条件?还是我哪里没掌握好?

谢谢

第一个测试是跳过单词之间的所有 spaces。它在到达第一个非 space,即单词的 开头 时停止。

内部 while 循环用于跳过字符,直到我们到达下一个 space,因此它找到该词的 end

注意这两个条件是相反的。第一个是*ch == ' ',第二个是*ch != ' '

*ch != '[=12=]'*ch && 用于在到达字符串末尾时停止(C 字符串以零字节结尾)。这在遍历字符串的任何循环中都是必需的,因此它不会超出结尾。

What I do not understand is why condition 1 which is : (*ch!='[=31=]') in the first while loop is tested again in the second while loop.

我将在这里重点介绍一些重要的细节:

while(*ch!='[=10=]') {
    if(*ch==' ') {
        ch++;                  // 1.
        continue;
    }
    c++;
    while(*ch && *ch !=' ') {
        ch++;                  // 2.
    }
}

12中,char*ch加1,这使得ch指向一个新地址——内容未知.

当再次到达 while 循环的起点时,它会测试 ch 指向的内容是否包含 null-terminator,它表示字符串的结尾。如果是,则循环结束 - 因为随后将对字符串进行全面检查。

所以,while(*ch!='[=19=]') 并不是一遍又一遍地测试同一件事。 ch 在每次循环后指向一个新的 char

你在内部循环中有相同的测试:

    while(*ch && *ch !=' '){    // <-here
        ch++;
    }

在这里,*ch也在做同样的测试,*ch != '[=23=]',但没有那么多话。

  • [=24=] 是空终止符的正确字符文字。
  • 空终止符具有实际值 0
  • 在布尔上下文中,0 的计算结果为 false,其他所有计算结果为 true
  • 因此,*ch 在布尔上下文中等于 *ch != '[=23=]'

这个问题说明了为什么在我看来,将事物分解成具有有意义名称的函数如此重要。

假设我们想要将字符串推进到前导白色space 字符之后。我们可以创建一个函数,它接受一个字符串 (char *),创建一个指向它开头的指针,递增该指针直到它遇到一个不是 space、制表符或换行符的字符, 然后 returns 那个指针。

char * first_nonspace(char * s) {
    char * iter = s;

    for (; *iter && (*iter == ' ' || *iter == '\t' || *iter == '\n'); 
           iter++);

    return iter;
}

以同样的方式可以使函数跳到下一个白色space字符。

char * first_space(char * s) {
    char * iter = s;

    for (; *iter && !(*iter == ' ' || *iter == '\t' || *iter == '\n'); 
           iter++);

    return iter;
}

现在统计字数就简单多了。我们的 for 循环甚至不需要主体。它以 iter 指向字符串的第一个字符开始,count 设置为零。当我们没有 运行 进入空字符,并且第一个非 space 字符不为空时,它会继续。

每次我们通过获取第一个非 space 字符进行更新,然后从该指针前进到第一个 space。这有效地迭代了一个“词”。同时,我们通过将计数递增 1 来更新它。

int main() {
    char * test = "     hello world foo bar      ";
    char * iter;
    int count;

    for (iter = test, count = 0; 
         *iter && *first_nonspace(iter); 
         iter = first_space(first_nonspace(iter)), count++);
    
    printf("Found %d words.\n", count);
} 

将大问题分解成小问题非常有助于理解正在发生的事情。我所描述的是在您的程序中发生的相同逻辑(尽管我添加了处理制表符和换行符)但是在具有更有用名称的可管理块中。