The type of the parameter needs to match the type of the expression you’re passing - in this case, you’d be passing an expression of type string
, so the prototype needs to be
int count_letters(string text)
To get that count, you have to call the function from main
. You can either do that from within printf
call:
printf("%i Letter(s) \n", count_letters(text));
or you can create another variable to store that result:
string text = get_string( ... );
int len = count_letters( text );
printf( "%i Letter(s) \n", len );
The variable countl
is local to the count_letters
function and is not accessible from main
.
<Gratuitous Rant>
The CS50 string
typedef name is a lie because what it aliases is not a string, and after seeing enough questions from people taking the CS50 course I believe it is actually a hinderance to learning C; it completely misrepresents how strings are represented and handled, and if you do any C programming outside of this course you will be completely unprepared for how string processing (and I/O in general) actually work.
Get comfortable, this is gonna take a while.
In C, a string is a sequence of characters including a zero-valued terminator. The string "hello"
is represented as the sequence {'h', 'e', 'l', 'l', 'o', 0}
.
Strings (including string literals like "hello"
) are stored in arrays of character type:
char text[] = "hello"; // array size is determined by the length of the initializer
or
char text[SOME_SIZE]; // where SOME_SIZE is large enough to store what we need
strcpy( text, "hello" );
Since strings are stored in arrays, you cannot use the =
operator to assign them (outside of an initializer as shown above, but that’s only valid for a declaration). You either need to use a library function like strcpy
(for arrays that contain strings) or memcpy
(for arrays that contain anything else), or you need to assign each element individually:
text[0] = 'h';
text[1] = 'e';
...
text[5] = 0;
Now, under most circumstances, an expression of type "array of T
" (including string literals like ”hello"
) will be converted ("decay") to an expression of type "pointer to T
" and the value of the expression will be the address of the first element. So, if we call a function like
count_letters( text );
it’s exactly the same as writing
count_letters( &text[0] );
and what count_letters
actually receives is a pointer of type char *
, and we’d declare the prototype as
int count_letters( char *str ) {...}
When you write something like
char *str = "hello";
you’re assigning the address of the first character of the string to str
, not the string contents themselves.
string
is a typedef name, or alias, for the type char *
defined in cs50.h
. It is not a part of the C language or standard C library. The problem is that char *
is not a string. It may point to the first character of a string. It may point to the first character of a sequence that is not a string. It may point to a single character that’s not part of a larger sequence. When we’re dealing with strings we often deal with expressions of type char *
, but an expression of type char *
is not, in and of itself, a string.
The get_string
function (again, part of the cs50
library and not a standard C library function) performs a lot of magic under the hood to dynamically allocate an array to store the string and returns a pointer to the first element (which is what you’re actually assigning). It’s slick, it’s handy, but again, it completely misrepresents how C actually does things.
</Gratuitous Rant>