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まで)であることと,符号付き数値型の負数の表現法が原因.とりあえずメモ.