singed char と unsigned char の違いに関するメモ

バイナリデータをごりごり直接操作する場合,1byte(8bit)型としてchar型を使用することがある.このとき,符号(signed / unsigned) を意識しないと思わぬ罠にはまるという話の経験談

結論

  • 1byte型は"unsigned" char型を意識して使うほうがよい.

ポイント

  • "char"のみと記述した場合,"signed" or "unsigned" のどちらであるのかは,言語仕様で定められていない
    • たとえば,VCではコンパイルオプションでどちらとみなすか指定できる.
  • char型の「文字型」としての機能を忘れない
    • 難しくいえば: バイナリは「具体」・文字は「抽象」

実験

C/C++でchar型の変数は,格納されているバイナリ値に対応するasciiコード表上の文字を表現している.たとえば,0x61というバイナリ値は'a'という文字を表現していると解釈される:

  std::cout << boost::format("%X") % (short)'a' << '\n'; // 0x61
  std::cout << boost::format("%s") % (unsigned char)97 << '\n'; // a
  std::cout << boost::format("%s") % (unsigned char)0x61 << '\n'; // a

実行結果:

61
a
a

つまり,数値(10進数/バイナリ値)をchar型にキャストすると文字として解釈が可能となる.

さて,数値をchar型にキャストするとき,その数値範囲を気を付ける必要がある.例として,127(0x7F)と128(0x80)を挙げる.

まず,"unsigned char"にキャストしたのちに,数値データに再キャストする:

  std::cout << boost::format("%1$d : %1$X") % (short)(unsigned char)127 << '\n';
  std::cout << boost::format("%1$d : %1$X") % (short)(unsigned char)128 << '\n';
127 : 7F
128 : 80

これは素直に解釈できて,127-->127, 128-->128と無事に戻ってこれる.

問題は,signed char型に,または,char型を"signed" char型と扱う環境でchar型に,キャストしたのち,数値データに再キャストした場合:

  std::cout << boost::format("%1$d : %1$X") % (short)(char)127 << '\n';
  std::cout << boost::format("%1$d : %1$X") % (short)(char)128 << '\n';
127 : 7F
-128 : FF80

ここでは,127-->127に戻るが,128-->"-128" になってしまう!

たぶん,文字の表現としてasciiコードが7bit(0x7Fまで)であることと,符号付き数値型の負数の表現法が原因.とりあえずメモ.