Aaryan BHAGAT
Aaryan BHAGAT

Reputation: 40

A problem with the behaviour of scanf when data type is unsigned char

So I have no idea why this behavior is observed:

#include <stdio.h>

int main()
{
    unsigned char c;
    char d;
    scanf("%hhu", &d);
    printf("%c\n", d);

    return 0;
}

If I remember correctly %hhu is for unsigned char. If I input almost any character like a, or b the answer is nothing and d gets the value 0. But if I input 97 then d prints the value a (so ASCII is converted to char). This clearly not the case when the data type is char. I can just input the character directly and it is stored.

Also in the slight modification of this code

#include <stdio.h>

int main()
{
    unsigned char c;
    char d;
    int g;
    scanf("%hhu", &d);
    printf("%c\n", d);
    scanf("%d", &g);
    printf("%d is g\n", g);

    return 0;
}

If I give first input as a character like w or a then it just skips the second scanf, but if the input is a number then it works normally.

Upvotes: 0

Views: 740

Answers (5)

ikegami
ikegami

Reputation: 385546

a is not a valid input for %hhu. An integer (in the Math sense) is expected. If we add error checking (code below), we get:

$ gcc -Wall -Wextra -pedantic a.c -o a && ./a
a
Number of assigned variables: 0
Can't read: Invalid input
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    unsigned char c;

    int rv = scanf("%hhu", &c);
    if (rv == EOF) {
       if (ferror(stdin)) {
          perror("Can't read");
       } else {
          fprintf(stderr, "Can't read: Premature EOF\n");
       }

       exit(1);
    }

    printf("Number of assigned variables: %d\n", rv);

    if (rv < 1) {
       fprintf(stderr, "Can't read: Invalid input\n");
       exit(1);
    }

    printf("%hhu\n", c);

    return 0;
}

Invalid input stays in the handle's buffer to be obtained by future reads. So an error in a early scanf can cause later scanf to fail.

If you wish to proceed after an error, you could simply read until you get a LF to clear any "junk" in the buffer.

void read_stdin_to_eol(void) {
   while (1) {
      int ch = getchar();
      if (ch == '\n' || ch == EOF)
         return ch;
   }
}

Upvotes: 2

chux
chux

Reputation: 153338

If I remember correctly %hhu is for unsigned char. If I input almost any character like 'a', or 'b' the answer is nothing and d gets the value 0.

"%hhu" expects input text to be numeric like "123". Then the value of 123 is save in d. d did not get the value of 0. d was not changed by the scanf("%hhu", &d); as input was invalid.

If you want to read a character into an unsigned char

unsigned char d;
if (scanf("%c", &d) == 1) Success();

If you want to read a numeric text into an unsigned char

unsigned char d;
if (scanf("%hhu", &d) == 1) Success();

In all cases, test the return value of scanf().


If I give first input as a character like 'w' or 'a' then it just skips the second scanf, but if the input is a number then it works normally.

When scanf() fails to convert input text per the provided specifiers, scanning stops and the offending text remains in stdin.

A common code idiom is to read remaining input and toss it until '\n'.

int ch;
while ((ch = getchar()) != '\n') && ch !- EOF) {
  ;
}

Upvotes: 2

Acorn
Acorn

Reputation: 26066

If I input almost any character like 'a', or 'b' the answer is nothing and d gets the value 0. But if I input 97 then d prints the value a. (so ASCII is converted to char) This clearly not the case when the data type is char.

What scanf does is not related to the type but to the format specifier you use (which in turn defines what types are expected in the arguments).

When you use %hhu, the input is read as a signed decimal integer, parsed as strtoul would, and stored in an unsigned char. This is why 97 is read correctly.

Further, in both examples you should take into account the return value to know whether there was any failure. That way you will be able to detect errors and do whatever is required, like asking the user again.

Upvotes: 3

Adrian Mole
Adrian Mole

Reputation: 51815

You are correct that the %hhu format specifier expects an unsigned char* argument. However, the u part of the format dictates that the input be interpreted as a decimal integer. To input the data as an (unprocessed) character, you should use the %c format specifier.

Upvotes: 5

MikeCAT
MikeCAT

Reputation: 75062

Format specifier %u is for reading decimal integer and %hhu is %u with length modifier added.

To read one character to char variable, use specifier %c.

Upvotes: 3

Related Questions