isalpha関数に限ったことではありませんが、<ctype.h>ヘッダで宣言されるis系関数は、次のようにint型の引数を取ります。
0 1 2 |
int isalpha(int c); |
このこと自体もよくある間違いなのですが、問題はここからです。すなわち、is系関数の引数がchar型だと事実誤認しているために、あるいはint型であることを知っていたとしても、実引数として渡すことができる値に制約があることを知らないために、危険なコードを書いてしまうことがよくあるのです。
is系関数に実引数として渡すことができる値は、0 ~ UCHAR_MAXまたはEOFだけです。通常は問題ないのですが、多バイト文字を含む文字列の各要素を順にis系関数で調べる場合などは、is系関数に負の値を渡してしまうことになります。
そうです。char型が符号付きか符合無しかは処理系定義ですので、char型が符号無しの環境でたまたま動いていたとしても、char型が符号付きの環境に移植したとたん、不可解な動作をしたり、クラッシュしたりすることになるのです。
is系関数に渡す実引数は、fgetc関数の返却値のような場合はそのままで構いませんが、char型の値を渡すときには、明示的にunsigned char型にキャストしなければなりません。
0 1 2 3 4 5 6 7 8 9 |
const char str[] = "abc123"; for (char* s = str; *s != '\0'; s++) { if (isalpha(static_cast<unsigned char>(*s))) { ... } } |
分かっていてもよく間違うもので、特に、述語としてis系関数をアルゴリズム関数に渡す場合など、コールバックでは要注意です。