The crucial point is that c must be an int:
int c;
while ((c = getchar()) != EOF) { char read_value = c; /* ... */ }
It is assumed that an int can hold more values than a char, or at least more values that the system's narrow multibyte encoding uses*, and getchar returns a special constant EOF when there it failed to read more data. Otherwise, it is guaranteed that you can convert c to a char and obtain the value of the character that was read.
It is a common mistake to declare c itself as a char, in which case the loop might never terminate, since you might not be able to capture the special value EOF, or otherwise there would be a perfectly valid character which would be indistinguishable from (char)EOF.
*) For example, it would be perfectly fine if both a char and an int were 32 bits wide on a given platform, as long as, say, the narrow stream could only return units with values in the range [-128, 128), and you could use -200 as EOF.