CheetahBongos CheetahBongos - 3 months ago 9
C Question

Don't understand function to count digits in character array

I am having a lot of trouble understanding my professor's solution to a program that counts the number of digits in a character array.

int main(void)
{
char* s[] = {"12, 34, 56, 78",
"82.16, 41.296",
"2, -3, 5, -7, 11, -13, 17, -19",
"9.00009, 90.0009, 900.009, 9000.09, 90000.9"};
int i, rows = sizeof(s)/sizeof(s[0]);

for(i = 0; i < rows; i++) {
digitCounts( s[i] );
}
}

void digitCounts(char* s) {
int counts[10] = { 0 };
int i = 0;

while( s[i] != '\0' )
{
if('0' <= s[i] && s[i] <= '9')
counts[ s[i] - '0' ]++;

i++;
}

for(i = 0; i < 9; i++)
printf("%d, ", counts[i]);

printf("%d\n\n", counts[i]);
}


Can someone please help me understand this section specifically:

while( s[i] != '\0' )
{
if('0' <= s[i] && s[i] <= '9')
counts[ s[i] - '0' ]++;

i++;
}


I understand the while-loop conditional: if the element at s[i] is not equal to NUL, then proceed. But the if statement is confusing to me because are you comparing numerical values or string values? How can a string value be greater than or less than another? e.g. '1' <= '9'. Also, wouldn't the first element of s[] be '12'? Not '1'. In other words, it wouldn't be a single digit.

Please help I am really lost!

Answer

The line

if('0' <= s[i] && s[i] <= '9')

is comparing individual characters within a single string against the character constants '0' and '9' (ASCII values 48 and 57).

Remember that a string in C is a sequence of character values followed by a 0-valued terminator, and string literals are delimited by double quotes ("). Strings (including string literals) are stored as arrays of char. Each element of s contains the address of the first character of an array of char, sort of like the following1:

   +---+         +---+---+---+
s: |   | ------> |'1'|'2'| 0 |
   +---+         +---+---+---+
   |   | ----+
   +---+     |   +---+---+---+    
   |   | -+  +-> |'3'|'4'| 0 |
   +---+  |      +---+---+---+ 
    ...   |
          |      +---+---+---+
          +----> |'5'|'6'| 0 |
                 +---+---+---+

Thus, s[0] points to the string "12", s[1] points to the string "34", etc. Each s[i] is passed to the digitCounts function, which walks through the string character by character and checks each to see if it is a digit.

The C language definition guarantees that the values of the digit characters '0', '1', '2', etc., are sequential, so any character value between '0' and '9' must be a digit character. So the condition

if ( '0' <= s[i] && s[i] <= '9' )

is checking to see if each s[i] is a digit character (character constants like '0' and '9' are delimited by single quotes). Note that there is a library function that does this same thing, with the bonus that it takes locale into account:

#include <ctype.h>
...
if ( isdigit( s[i] ) ) // does the same thing as the line above.   

Your professor's code is ... not awesome. It would have been nice if he used different names for the array of strings in main (such as numberStringList) vs. the input parameter to digitCounts (such as numberString); I think that accounts for some of your confusion. And he should have shown you how to use the isdigit function instead of hand-hacking the check.


  1. In main, s is declared as an array of pointer to char (char *[]). The size of the array is taken from the number of initializers in the initializer list.

    Each string literal in the initializer is also an array of char, but under most circumstances array expressions are converted ("decay") to pointer expressions, and the value of the expression is the address of the first element of the array. So each s[i] stores the address of the string literal, not the literal itself.