【C言語】memchr関数のunsigned charを理解する
きっかけ
memchr関数を自作しようとしたところ、再現にはunsigned charの理解が必要だったため、いろいろ試した過程をまとめました。、
memchr関数の定義は以下です。
void *memchr(const void *s, int c, size_t n);
メモリ内の文字が、引数cと同じか比較する関数です。
文字はASCIIでは10進数の場合、0(NULL)~127(DEL)までですが、引数cをマイナスにした場合や、*sに0~127を入れたらどうなるでしょうか。
上記のような、ほぼありえなさそうなケースも念のためテストしたところ、結果的に再現できていない箇所を発見できました。その原因がunsigned charという型でした。
元の自作関数(再現できてなかった)
再現できていないことが発覚したときのコードは以下です。
void *mymemchr(const void* s, int c, size_t n)
{
unsigned char *str;
str = (unsigned char *)s;
if (str == NULL)
return (NULL);
while (n--)
{
if (*str == c)
return (str);
str++;
}
return (NULL);
}
int main(void){
unsigned char s[] = "abcdefgh";
char *p1;
char *p2;
int c =-120;
int n = 8;
s[2]=-120; //文字列sの'c'を-120に変更
p1 = memchr(s,c, n);
printf("memchr = %s\n",p1);
p2 = mymemchr(s,c, n);
printf("mymemchr = %s\n",p2);
return 0;
}
実際の関数、自作した関数の結果は以下のとおり、異なっていました。
memchr = �defgh
mymemchr = (null)
再現できてなかった原因
原因は引数cをint型のままにしていたことでした。
自作前の下調べで、「memchr関数は引数s,cをunsigned charとして比較する」と見た記憶がありました。unsigned charの理解を後回しにした自覚があったので原因特定は早かったです。
原因特定のための確認用コードは以下です。s,cの値が自作コードの中でどのようにな挙動か確認します。
void *mymemchr(const void* s, int c, size_t n)
{
unsigned char *str;
str = (unsigned char *)s;
printf("str[2]=%d, c=%d\n",str[2],c); //確認用コードを追加
if (str == NULL)
return (NULL);
while (n--)
{
if (*str == c)
return (str);
str++;
}
return (NULL);
}
//mainは同じ
確認用コードの結果は以下のとおりでした。
str[2]=136, c=-120
unsigned char型のstr[2]では、代入した-120は136に変わっていました。
unsigned charの範囲は、0~255です。
-120は136に変わった理由は、オーバーフロー?したためのようです。つまり、以下のような挙動を示します。
unsigned char *s=-1 // s=255
unsigned char *s=-2 // s=254
unsigned char *s=-120 // s=136
解決方法
引数cを、論理和(&演算子)を使ってunsigned charと同じ0~255の範囲にします。
int main()
{
int a,b,c,d,e;
a = 0;
a &= 255;
printf("a=%d\n",a);
b = -120;
b &= 255;
printf("b=%d\n",b);
c = 255;
c &= 255;
printf("c=%d\n",c);
d = 450;
d &= 255;
printf("d=%d\n",d);
e = -4450;
e &= 255;
printf("e=%d\n",e);
return(0);
}
結果は以下のとおりです。
a=0
b=136
c=255
d=194
e=158
修正後の自作関数
void *mymemchr(const void* s, int c, size_t n)
{
unsigned char *str;
c &= 0xff;
str = (unsigned char *)s;
printf("str[2]=%d, c=%d\n",str[2],c); //確認用コード残し
if (str == NULL)
return (NULL);
while (n--)
{
if (*str == c)
return (str);
str++;
}
return (NULL);
}
//mainは同じ
結果は以下のとおりです。
memchr = �defgh
str[2]=136, c=136
mymemchr = �defgh
引数s、cが想定外の数値でも再現ができるようになりました。
参考にしたサイト
Discussion