負数に対してシフト演算を行う際の注意

ワード単位で操作したいので,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が埋められていることがわかる.
トルエンディアンのため,バイト毎の順番が逆になっている.

今回の教訓

負数に対する右シフト演算は避けるべし