Isdigit(C) - a Char or Int Type

isdigit(c) - a char or int type?

The situation is unfortunately a bit more complex than has been told by the other answers.

First of all: the first part of your code is correct (disregarding multiple-byte encodings); if you want to read a single char with cin, you'll have to use a char variable with >> operator.

Now, about isdigit: why does it take an int instead of a char?

It all comes from C; isdigit and its companion were born to be used along with functions like getchar(), which read a character from the stream and return an int. This in turn was done to provide the character and an error code: getchar() can return EOF (which is defined as some implementation-defined negative constant) through its return code to signify that the input stream has ended.

So, the basic idea is: negative = error code; positive = actual character code.

Unfortunately, this poses interoperability problems with "regular" chars.

Short digression: char ultimately is just an integral type with a very small range, but a particularly stupid one. In most occasions - when working with bytes or character codes - you'd want it to be unsigned by default; OTOH, for coherency reasons with other integral types (int, short, long, ...), you may say that the right thing would be that plain char should be signed. The Standard chose the most stupid way: plain char is either signed or unsigned, depending from whatever the implementor of the compiler decides1.

So, you have to be prepared for char being either signed or unsigned; in most implementations it's signed by default, which poses a problem with the getchar() arrangement above.

If char is used to read bytes and is signed it means that all bytes with the high bit set (AKA bytes that, read with an unsigned 8-bit type would be >127) turn out to be negative values. This obviously isn't compatible with the getchar() using negative values for EOF - there could be overlap between actual "negative" characters and EOF.

So, when C functions talk about receiving/providing characters into int variables the contract is always that the character is assumed to be a char that has been cast to an unsigned char (so that it is always positive, negative values overflowing into the top half of its range) and then put into an int. Which brings us back to the isdigit function, which, along its companion functions, has this contract as well:

The header <ctype.h> declares several functions useful for classifying and mapping characters. In all cases the argument is an int, the value of which shall be representable as an unsigned char or shall equal the value of the macro EOF. If the argument has any other value, the behavior is undefined.

(C99, §7.4, ¶1)

So, long story short: your if should be at the very least:

if(isdigit((unsigned char)c))

The problem is not just a theoretical one: several widespread C library implementations use the provided value straight as an index into a lookup table, so negative values will read into unallocated memory and segfault your program.

Also, you are not taking into account the fact that the stream may be closed, and thus >> returning without touching your variable (which will be at an uninitialized value); to take this into account, you should check if the stream is still in a valid state before working on c.


  1. Of course this is a bit of an unfair rant; as @Pete Becker noted in the comment below, it's not like they were all morons, but just that the standard mostly tried to be compatible with existing implementations, which were probably evenly split between unsigned and signed char. Traces of this split can be found in most modern compilers, which can generally change the signedness of char through command line options (-fsigned-char/-funsigned-char for gcc/clang, /J in VC++).

How to check int or char in c

isalpha and isdigit are applied to objects of the type int that contain a char.

You are trying to apply these functions to an object of the type char * (an array type is implicitly converted to a pointer type in expressions).

Moreover the array t declared like

char t[] = "";

is not enough large to store even one character gotten from scanf because it also need to store the terminating zero. Otherwise a call of scanf will have undefined behavior. And the call of scanf is also incorrect.

scanf("%s", &t);
^^^

It should be written at least like

scanf("%s", t);

You could declare an object of the type char like

char t;

and then use scanf like

scanf(" %c", &t);

and at last

if ( isalpha( ( unsigned char )t ) )
{
printf("It's char\n");
}
else if ( isdigit( ( unsigned char )t ) )
{
printf("It's int\n");
}

int validation using scanf and isdigit

Don't use isdigit() — it is for checking whether a character is a digit or not. You've read a number. You must check the return value from scanf() — if it is not 1, you've got a problem. Depending on your requirements, the fact that there may be all sorts of stuff on the line after the number may or may not be a problem. I'm assuming when you say "validate the input is an integer", you want to allow for multiple-digit numbers, and negative numbers, and since you used %i rather than %d, you're fine with octal values (leading 0) or hexadecimal values (leading 0x or 0X) being entered too.

Note that if you have:

int checker = scanf("%i", &i);

then the result could be 1, 0, or EOF. If the result is 1, then you got an integer after possible leading white space, including possibly multiple newlines. There could be all sorts of 'garbage' after the number and before the next newline. If the result is 0, then after skipping possible white space, including possibly multiple newlines, the first character that wasn't white space also wasn't part of an integer (or it might have been a sign not immediately followed by a digit). If the result is EOF, then end-of-file was detected after possibly reading white space, possibly including multiple newlines, but before anything other than white space was read.

To continue sensibly, you need to check that the value returned was 1. Even then, there could be problems if the value is out of the valid range for the int type.

The full requirement isn't completely clear yet. However, I'm going to assume that the user is required to enter a number on the first line of input, with possibly a sign (- or +), and possibly in octal (leading 0) or hexadecimal (leading 0x or 0X), and with at most white space after the number and before the newline. And that the value must be in the range INT_MIN .. INT_MAX? (The behaviour of scanf() on overflow is undefined — just to add to your woes.)

The correct tools to use for this are fgets() or POSIX
getline() to read the line, and
strtol() to convert to a number, and isspace() to validate the tail end of the line.

Note that using strtol() properly is quite tricky.

In the code below, note the cast to unsigned char in the call to isspace(). That ensures that a valid value is passed to the function, even if the plain char type is signed and the character entered by the user has the high bit set so the value would convert to a negative integer. The valid inputs for any of the ispqrst() or topqrst() functions are EOF or the range of unsigned char.

C11 §7.4 Character handling <ctype.h> ¶1

… In all cases the argument is an int, the value of which shall be representable as an unsigned char or shall equal the value of the macro EOF. If the argument has any other value, the behavior is undefined.

The GNU C library tends to protect the careless, but you should not rely on being nannied by your standard C library.

#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
char line[4096]; /* Make it bigger if you like */

printf("Enter a number: ");
if (fgets(line, sizeof(line), stdin) == 0)
{
fprintf(stderr, "Unexpected EOF\n");
exit(EXIT_FAILURE);
}
errno = 0;
char *eon; /* End of number */
long result = strtol(line, &eon, 0);
if (eon == line)
{
fprintf(stderr, "What you entered is not a number: %s\n", line);
exit(EXIT_FAILURE);
}
if (sizeof(int) == sizeof(long))
{
if ((result == LONG_MIN || result == LONG_MAX) && errno == ERANGE)
{
fprintf(stderr, "What you entered is not a number in the range %ld..%+ld: %s\n",
LONG_MIN, LONG_MAX, line);
exit(EXIT_FAILURE);
}
}
else
{
if ((result == LONG_MIN || result == LONG_MAX) && errno == ERANGE)
{
fprintf(stderr, "What you entered is not a number in the range %ld..%+ld,\n"
"let alone in the range %d..%+d: %s\n",
LONG_MIN, LONG_MAX, INT_MIN, INT_MAX, line);
exit(EXIT_FAILURE);
}
}
char c;
while ((c = *eon++) != '\0')
{
if (!isspace((unsigned char)c))
{
fprintf(stderr, "There is trailing information (%c) after the number: %s\n",
c, line);
exit(EXIT_FAILURE);
}
}
if (result < INT_MIN || result > INT_MAX)
{
fprintf(stderr, "What you entered is outside the range %d..%+d: %s\n",
INT_MIN, INT_MAX, line);
exit(EXIT_FAILURE);
}
int i = result; /* No truncation given prior tests */
printf("%d is a valid int\n", i);
return(EXIT_SUCCESS);
}

That seems to work correctly for a fairly large collection of weird numeric and non-numeric inputs. The code handles both 32-bit systems where sizeof(long) == sizeof(int) and LP64 64-bit systems where sizeof(long) > sizeof(int) — you probably don't need that. You can legitimately decide not to detect all the separate conditions but to aggregate some of the errors into a smaller number of error messages.

Check if a char is a digit? (in C)

The trick is that the isdigit function does not take an argument of type char. Quoting the standard (N1570 7.4p1:

The header <ctype.h> declares several functions useful for
classifying and mapping characters. In all cases the argument is an
int, the value of which shall be representable as an unsigned char or shall equal the value of the macro EOF. If the
argument has any other value, the behavior is undefined.

The type char may be either signed or unsigned. If it's signed (as it very commonly is), then it can hold negative values -- and passing a negative value other than EOF (typically -1) to isdigit, or to any of the other functions declared in <ctype.h>, causes undefined behavior.

The solution is to convert the argument to unsigned char before passing it to isdigit:

char c = -46;
if (isdigit((unsigned char)c) {
puts("It's a digit (?)");
}
else {
puts("It's not a digit");
}

And yes, this is exactly as annoying and counterintuitive as you think it is.

Why is parameter to isdigit integer?

The reaons is to allow EOF as input. And EOF is (from here):

EOF integer constant expression of type int and negative value



Related Topics



Leave a reply



Submit