負数に対してシフト演算を行う際の注意
ワード単位で操作したいので,4バイト型として(signed) int型を利用していた.符号なんて別に関係ないだろう,そう思っていた時期が僕にもありました.
シフト演算を行う際に符号あり型の場合,右シフトした際の左ビットは0で埋められるとは限らない.1で埋められるとも限らない.(今回のケースでは1で埋められた)
確認コード
#include <stdio.h> int main (void) { int a = -1; // unsigned int b = -1; fwrite(&a, sizeof(int), 1, stdout); // fwrite(&b, sizeof(int), 1, stdout); a >>= 8; // b >>= 8; fwrite(&a, sizeof(int), 1, stdout); //fwrite(&b, sizeof(int), 1, stdout); return 0; }
符号ありの場合 (int a)
% ./a.out | xxd -b 0000000: 11111111 11111111 11111111 11111111 11111111 11111111 ...... 0000006: 11111111 11111111
負の値を持つsigned int変数に対して右シフトを行う場合,1で埋められるということがわかる.
符号なしの場合 (int b)
% ./a.out | xxd -b 0000000: 11111111 11111111 11111111 11111111 11111111 11111111 ...... 0000006: 11111111 00000000
こちらは右シフトによって左8ビットが0が埋められていることがわかる.
リトルエンディアンのため,バイト毎の順番が逆になっている.
今回の教訓
負数に対する右シフト演算は避けるべし