Reputation: 2258
I wrote some code to verify a serial number is alpha numeric in C using isalnum
. I wrote the code assuming isalnum
input is char
. Everything worked. However, after reviewing the isalnum
later, I see that it wants input as int
. Is my code okay the way it is should I change it?
If I do need to change, what would be the proper way? Should I just declare an int and set it to the char and pass that to isalnum
? Is this considered bad programming practice?
Thanks in advance.
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
bool VerifySerialNumber( char *serialNumber ) {
int num;
char* charPtr = serialNumber;
if( strlen( serialNumber ) < 10 ) {
printf("The entered serial number seems incorrect.");
printf("It's less than 10 characters.\n");
return false;
}
while( *charPtr != '\0' ) {
if( !isalnum(*charPtr) ) {
return false;
}
*charPtr++;
}
return true;
}
int main() {
char* str1 = "abcdABCD1234";
char* str2 = "abcdef##";
char* str3 = "abcdABCD1234$#";
bool result;
result = VerifySerialNumber( str1 );
printf("str= %s, result=%d\n\n", str1, result);
result = VerifySerialNumber( str2 );
printf("str= %s, result=%d\n\n", str2, result);
result = VerifySerialNumber( str3 );
printf("str= %s, result=%d\n\n", str3, result);
return 0;
}
Output:
str= abcdABCD1234, result=1
The entered serial number seems incorrect.It's less than 10 characters.
str= abcdef##, result=0
str= abcdABCD1234$#, result=0
Upvotes: 1
Views: 6645
Reputation: 279445
Usually it is fine to pass a char
value to a function that takes an int
. It will be converted to the int
with the same value. This isn't a bad practice.
However, there is a specific problem with isalnum
and the other C functions for character classification and conversion. Here it is, from the ISO/IEC 9899:TC2 7.4/1 (emphasis mine):
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 macroEOF
. If the argument has any other value, the behavior is undefined.
So, if char
is a signed type (this is implementation-dependent), and if you encounter a char
with negative value, then it will be converted to an int
with negative value before passing it to the function. Negative numbers are not representable as unsigned char
. The numbers representable as unsigned char
are 0
to UCHAR_MAX
. So you have undefined behavior if you pass in any negative value other than whatever EOF
happens to be.
For this reason, you should write your code like this in C:
if( !isalnum((unsigned char)*charPtr) )
or in C++ you might prefer:
if( !isalnum(static_cast<unsigned char>(*charPtr)) )
The point is worth learning because at first encounter it seems absurd: do not pass a char
to the character functions.
Alternatively, in C++ there is a two-argument version of isalnum
in the header <locale>
. This function (and its friends) do take a char
as input, so you don't have to worry about negative values. You will be astonished to learn that the second argument is a locale ;-)
Upvotes: 3
Reputation: 4612
You don't need to change it. The compiler will implicitly convert your char
to an int
before passing it to isalnum
. Functions like isalnum
take int
arguments because functions like fgetc
return int
values, which allows for special values like EOF
to exist.
Update: As others have mentioned, be careful with negative values of your char
. Your version of the C library might be implemented carefully so that negative values are handled without causing any run-time errors. For example, glibc
(the GNU implementation of the standard C library) appears to handle negative numbers by adding 128 to the int
argument.* However, you won't always be able to count on having isalnum
(or any of the other <ctype.h>
functions) quietly handle negative numbers, so getting in the habit of not checking would be a very bad idea.
* Technically, it's not adding 128 to the argument itself, but rather it appears to be using the argument as an index into an array, starting at index 128, such that passing in, say, -57
would result in an access to index 71
of the array. The result is the same, though, since array[-57+128]
and (array+128)[-57]
point to the same location.
Upvotes: 10