2

My friend asked me what is (char)getchar() which he found in some online code and I googled and found 0 results of it being used, I thought the regular usage is just ch = getchar(). This is the code he found, Can anyone explain what is this function?

else if (input == 2)
{
    if (notes_counter != LIST_SIZE)
    {
        printf("Enter header: ");
        getchar();
        char c        = (char)getchar();
        int tmp_count = 0;
        while (c != '\n' && tmp_count < HEADER)
        {
            note[notes_counter].header[tmp_count++] = c;
            c = (char)getchar();
        }
        note[notes_counter].header[tmp_count] = '\0';
        
        printf("Enter content: ");
        c         = (char)getchar();
        tmp_count = 0;
        while (c != '\n' && tmp_count < CONTENT)
        {
            note[notes_counter].content[tmp_count++] = c;
            c = (char)getchar();
        }
        note[notes_counter].content[tmp_count] = '\0';
        
        printf("\n");
        notes_counter++;
    }
}
imxitiz
  • 3,920
  • 3
  • 9
  • 33
rewed
  • 61
  • 1
  • 8
    [`getchar`](https://en.cppreference.com/w/c/io/getchar) returns an `int` (since it can also return `EOF` which is outside the range of a `char`) - without the cast you should be getting a compiler warning about an implicit narrowing conversion – UnholySheep Jun 24 '22 at 13:33
  • Note that code like `while (c != '\n' && tmp_count < HEADER) { note[notes_counter].header[tmp_count++] = c; c = (char)getchar(); }` does not behave well if it encounters EOF before a newline. The code should use `int c;` instead of `char c;` and should test the integer against EOF. – Jonathan Leffler Jun 24 '22 at 13:37
  • 1
    *"...which he found in some online code..."* - there is the first problem. Be very wary of "online code", the person posting it may have much less experience than you do with the language. "Online References", like for example [C11 Standard](http://port70.net/~nsz/c/c11/n1570.html) are good authoritative references to consult. Specifically [7.21.7 Character input/output functions](http://port70.net/~nsz/c/c11/n1570.html#7.21.7) will answer your question. – David C. Rankin Jun 24 '22 at 13:41
  • 1
    [Why does fgetc() return int instead of char?](https://stackoverflow.com/q/49063518/995714) – phuclv Jun 24 '22 at 13:44
  • The only purpose of `(char) getchar()` is to indicate to the reader that the code should be discarded and that the author is not to be trusted. It is a novice mistake. – William Pursell Jun 24 '22 at 14:54

2 Answers2

5

(char)getchar() is a mistake. Never use it.

getchar returns an int that is either an unsigned char value of a character that was read or is the value of EOF, which is negative. If you convert it to char, you lose the distinction between EOF and some character that maps to the same char value.

The result of getchar should always be assigned to an int object, not a char object, so that these values are preserved, and the result should be tested to see if it is EOF before the program assumes a character has been read. Since the program uses c to store the result of getchar, c should be declared as int c, not char c.

It is possible a compiler issued a warning for c = getchar(); because that assignment implicitly converts an int to a char, which can lose information as mentioned above. (This warning is not always issued by a compiler; it may depend on warning switches used.) The correct solution for that warning is to change c to an int, not to insert a cast to char.

About the conversion: The C standard allows char to be either signed or unsigned. If it is unsigned, then (char) getchar() will convert an EOF returned by getchar() to some non-negative value, which will be the same value as one of the character values. If it is signed, then (char) getchar() will convert some of the unsigned char character values to char in an implementation-defined way, and some of those conversions may produce the same value as EOF.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
3

The code is a typical example of incorrect usage of the getchar() function.

getchar(), and more generally getc(fp) and fgetc(fp) return a byte from the stream as a positive value between 0 and UCHAR_MAX or the special negative value EOF upon error or end of file.

Storing this value into a variable of type char loses information. It makes testing for EOF

  • unreliable if type char is signed: if EOF has the value (-1) it cannot be distinguished from a valid byte value 255, which most likely gets converted to -1 when stored to a char variable on CPUs with 8-bit bytes

  • impossible on architectures where type char is unsigned by default, on which all char values are different from EOF.

In this program, the variables receiving the getchar() return value should have type int.

Note also that EOF is not tested in the code fragment, causing invalid input strings such as long sequences of ÿÿÿÿÿÿÿÿ at end of file.

Here is a modified version:

else if (input == 2)
{
    if (notes_counter != LIST_SIZE)
    {
        int c;
        // consume the rest of the input line left pending by `scanf()`
        // this should be performed earlier in the function
        while ((c = getchar()) != EOF && c != '\n')
            continue;

        printf("Enter header: ");
        int tmp_count = 0;
        while ((c = getchar()) != EOF && c != '\n') {
            if (tmp_count + 1 < HEADER)
                note[notes_counter].header[tmp_count++] = c;
        }
        note[notes_counter].header[tmp_count] = '\0';
        
        printf("Enter content: ");
        tmp_count = 0;
        while ((c = getchar()) != EOF && c != '\n')
            if (tmp_count + 1 < CONTENT)
                note[notes_counter].content[tmp_count++] = c;
        }
        note[notes_counter].content[tmp_count] = '\0';
        
        printf("\n");
        notes_counter++;
    }
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • Re “`EOF` cannot be distinguished from a valid byte value 255”: You seem to assume `EOF` is −1, and converting 255 to signed `char` will produce −1. `EOF` can be any negative `int` value, and the conversion of an out-of-range value to a signed integer type is implementation-defined. – Eric Postpischil Jun 24 '22 at 13:45
  • @EricPostpischil: `EOF` is defined as `(-1)` on all platforms I have seen so far. Converting a byte value of `255` to a signed `char` is indeed implementation defined, which is unfortunate as characters read from a stream almost always get stored into a `char` array. An implementation that would store a value such that `(unsigned char)(char)255 != 255` would be insane. – chqrlie Jun 24 '22 at 14:02
  • Re “`EOF` is defined as (-1) on all platforms I have seen so far.”: So? “I have only seen X, therefore X” is not a valid engineering principle. The C standard is clear: C 2018 7.21.1 3 says `EOF` has a negative `int` value and nothing further. There is no reason to add any assumption to that; it cannot provide any benefit and can only cause bugs. – Eric Postpischil Jun 24 '22 at 14:19
  • Re “store a value”: Storing bytes in a `char` array is different from converting to `char`. Bytes in an array will be reinterpreted according to the encoding of their type. Conversions are performed in an implementation-defined manner. And, no, other methods of conversion are not insane. Clamping instead of wrapping is sensible. Trapping for out-of-range is sensible. – Eric Postpischil Jun 24 '22 at 14:21
  • @EricPostpischil: I agree that `EOF` could be any negative value and it would be safer if it were outside the range of type `char`, but I have never seen it implemented this way... `char c = 255;` converts an out of range `int` value to type `char`. There is no *reinterpretation* involved in this case. – chqrlie Jun 24 '22 at 14:43