Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using while(str[n++]!='\0') for counting the length of a string

Tags:

c

Please ignore the Japanese there.
I attempt to count the length of the string entered from stdin using the following code. But it didn't work expectedly:

#include <stdio.h>

int main(int argc, const char *argv[]) {
    char str[100];

    printf("文字列を入力してください:");  // Please enter a string:
    fgets(str,99,stdin);

    int n = 0;
    while (str[n++] != '\0');
    printf("文字列の長さは%dです\n", n);  // The length of the string is %d\n
    return 0; 
}

For example, if I enter glacious, I'll get n=10, which I expected to be n=8.
I understand that n++ will increment n after str[n++] != '\0' gets evaluated, and \0 is the default character appended to every string. But somehow this doesn't make sense to me. I know I can make this work for my purpose by adding n-=2 at the end, but I really want to understand what's going on here. Many thanks in advance!

like image 353
Mike Chen Avatar asked Dec 17 '22 12:12

Mike Chen


2 Answers

"I attempt to count the length of the string entered from stdin"..."I know I can make this work for my purpose by adding n-=2 at the end, but I really want to understand what's going on here. "

Documentation for fgets() includes the following:

"...reads a line from the specified stream and stores it into the string pointed to by str. It stops when either (n-1) characters are read, the newline character is read, or the end-of-file is reached, whichever comes first."

This call, without checking the return value of the function, and by passing an incorrect value for the length of the string, limits the potential of detecting errors, and introduces the possibility of undefined behavior. To address these issues, change this:

fgets(str,99,stdin); 

To, for example this:

if( fgets (str, sizeof str, stdin) != NULL ) 
{    
     ...

Dissecting the following: given user input value: "glacious", str looks like this in memory:

|g|l|a|c|i|o|u|s|\n|\0|?|...|?|
 0 1 2 3 4 5 6 7 8  9 10    99 

int n = 0;
while(str[n++] != '\0');

iterations:

    n at start                       n at finish
  • 1st: n==0, str[0] (g) != \0, n++, n==1
  • 2nd: n==1, str[1] (l) != \0, n++, n==2
  • 3rd: n==2, str[2] (a) != \0, n++, n==3
  • 4th: n==3, str[3] (c) != \0, n++, n==4
  • 5th: n==4, str[4] (i) != \0, n++, n==5
  • 6th: n==5, str[5] (o) != \0, n++, n==6
  • 7th: n==6, str[6] (u) != \0, n++, n==7
  • 8th: n==7, str[7] (s) != \0, n++, n==8
  • 9th: n==8, str[8] (\n) != \0, n++, n==9
  • 10th: n==9, str[9] (\0) == \0, n++, n==10

Clearly illustrates the state of all iterations, including the final post-increment of n, bringing it's total to 10 for a user input assumed to be only 8 characters. The \n and the final post-increment ( for \0) account for the additional value to n`. In summary the problem is simply adjusting your expectations to account for all characters in the buffer, including the ones you do not see.

Of interest, counting value of n does not equate to measuring the string length of str, for which the idiomatic method ( strlen() ), will yield 9. Given the definition of a C string, the following shows varying results for each corresponding method of looking at str, assuming initialization:

char str[100] = {0}; 

And str contents are: "glacious\n"//null terminator is implied

//method to calculate n in discussion above
//                         //yields n == 10
int len = strlen(str);     //yields n == 9
//after using strcspn()
str[strcspn(str, "\n")] = 0;
len = strlen(str);         //yields n == 8
size_t size = sizeof str;  //yields size == 100

As an aside, if goal is to count the number of entries, and if an alternative approach is okay, consider simplifying the method...

Replacing this section:

char str[100];

printf("文字列を入力してください:");
fgets(str,99,stdin);

int n = 0;
while(str[n++] != '\0');
printf("文字列の長さは%dです\n", n);
return 0; 

With this one which will break the loop upon seeing \n (newline character), or EOF (-1) (#define in stdio.h), resulting in a correct count of user inputs (minus newline):

int count = 0;
printf("Please enter a string:");
int c = fgetc(stdin);
while(( c != '\n') && (c != EOF))
{
    count++; //only increments when c meets criteria
    fputc(c, stdout);
    c = fgetc(stdin);
}
printf("\n\nThe length of the string is: %d\n", count);
return 0; 
like image 123
ryyker Avatar answered Dec 31 '22 00:12

ryyker


If fgets() encounters a newline character in stdin, it will write it to the end of str before the null terminator, and because you're using a post-increment operator in the condition expression of your while loop, you are also including the null terminator in your total count. This accounts for the difference of 2 from your expected value n.

Consider using strcspn(), found in the <string.h> header, so that you can compute the length of str up until the first encountered \n, or until the null terminator if none is found:

size_t n = strcspn(str, "\n");
like image 37
Patrick Roberts Avatar answered Dec 31 '22 02:12

Patrick Roberts