Home > database >  Retesting while loop condition inside a nested while loop
Retesting while loop condition inside a nested while loop

Time:10-09

in this program I don't really understand why we test condition 1 of the first while loop in the nested while loop

The program is made to say how many words are in a string, words are separated by spaces

Here's the code:

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

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

Do I always have to test back conditions in nested while loops ? Or is there is there something I didn't grasp properly ?

Thanks

CodePudding user response:

The first test is for skipping over all the spaces between words. It stops when it gets to the first non-space, i.e. the beginning of a word.

The inner while loop is for skipping characters until we get to the next space, so it finds the end of that word.

Notice that the two conditions are opposite. The first one is *ch == ' ', the second one is *ch != ' '.

*ch != '\0' and *ch && are used to stop when we get to the end of the string (C strings end with a zero byte). That's needed in any loop that steps through the string so it doesn't go beyond the end.

CodePudding user response:

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

I'll focus on some important details here:

while(*ch!='\0') {
    if(*ch==' ') {
        ch  ;                  // 1.
        continue;
    }
    c  ;
    while(*ch && *ch !=' ') {
        ch  ;                  // 2.
    }
}

In both 1 and 2, the char* ch increases by one, which makes ch point at a new address - with unknown content.

When the start of the while loop is reached again, it tests if what ch is pointing at contains the null-terminator which signifies the end of the string. If it does, the loop ends - because the string is then fully examined.

So, while(*ch!='\0') is not testing the same thing over and over again. ch points at a new char after every loop.

You have the same test in the inner loop:

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

Here, *ch is doing the same test, *ch != '\0', but in not so many words.

  • \0 is the proper character literal for the null-terminator.
  • The null-terminator has the actual value 0 in all systems you need to care about.
  • In boolean context, 0 evaluates to false and anything else evaluates to true.
  • Hence, *ch equals *ch != '\0' in boolean contexts

CodePudding user response:

This question demonstrates why, in my opinion, breaking things out into functions that can have meaningful names is so important.

Let's say we want to advance a string past leading whitespace characters. We can create a function that takes a string (char *), creates a pointer to point to the start of it, increments that pointer until it hits a character that is not a space, tab, or newline, then returns that pointer.

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

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

    return iter;
}

In the same way a function can be made to skip to the next whitespace character.

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

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

    return iter;
}

Now counting the words is much simpler. Our for loop doesn't even need a body. It starts with iter pointing to the first character of the string, and count set to zero. It continues while we haven't run into the null character, and while the first non-space character isn't null.

Each time through we update by getting the first non-space character, and then from that pointer advancing to the first space. This effectively iterates over a "word." At the same time we update count by incrementing it by one.

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);
} 

Breaking big problems down into smaller ones is so helpful to understanding what's going on. What I've described is the same logic going on in your program (though I added handling tabs and newlines) but in manageable chunks with more useful names.

  • Related