【C言語】char が signed とは限らなかったという話 (C言語のおおらかな仕様に右往左往する日々)
発端
こちらの記事でご指摘頂いたことが始まりでした。
ご指摘内容は、次のコードが無限ループになっているというもの。
char c;
for (c = 1; c < 0x80; ++c)
char の範囲が
-128 ~ 127
であるとするならば、それは常に
0x80 = 128
よりも小さくなります。
このため、次の条件は必ず真であり、偽になることがありません。
c < 0x80
ただ、私の環境では無限ループにならなかった。
ご指摘を受けて改めて確認しましたが、やはり無限ループにはなりませんでした。
何故、私の環境では無限ループにならないのか。
調べてみました。
結論
結論から言います。
char 型は signed とは限らない
C言語の仕様では char が signed と規定されているわけではないのだそうです。
C言語の仕様上、次の3つは別の型として扱われるらしい。
char
signed char
unsigned char
仰け反りました。30 年以上もの間C言語を書きまくってきたけれども、全く知りませんでした。いや、聞いたけどすっかり忘れているのか。
私は主に制御系の組み込みソフトウェアを開発してきました。「制御系組み込みソフトウェア」の場合、 signed を使うことは、 char に限らず、 short でも、 int でも、ほとんどありません。また、型はほぼ 100% typedef で別に定義してきました。定義した型名称で、次の型のどれかの型しか使っていないと言ってもいい。
・unsigned char
・unsigned short
・unsigned long
今はどうかわからないけど昔は int 型のサイズがコンパイラによってブレがあったので、 int 型は使わない方がいいと言われていた。なので int 型もほとんど使いません。一方で、C言語は演算する度に int 型になってしまうので、演算すると結局どの型になるのかしばしば悩まされます。
それでもできるだけ「プラットフォームに依存しないコード」である方が望ましい。すると int を使うのは躊躇われる。プラットフォームに全く依存しないというのは難しいですが、昨今の H/W の進歩を考えれば、いつ CPU の変更を強いられるかはわかりません。
それにしても・・・。
char 型がプラットフォーム依存とは。
例えば、 gcc の場合・・・
次のような記述があります。
『signed char と unsigned char のどちらが「プレーン」char と同じ範囲、表現、および動作を持つか』は、ABI によって決定される、と。
ABI というのは「Application Binary Interface」の略で、「Binary」、すなわち「マシン語」のことだそうです。
同じコンパイラでも、X86 向けにコンパイルした場合と、ARM 向けにコンパイルした場合で異なるということになります。
いくつか読んだ限りでは、特に ARM は unsigned であることが多いらしい。今回の私のケースがまさにそれで、 clang コンパイラで ARM にコンパイルしたのですが、この場合 unsigned char で展開されたようです。
ちなみに、上記リンク先には「by ABI」が他にもいろいろありました。プラットフォーム依存はこれに限らない。
それにしてもよくわからないのは、何故 ARM だと unsigned char なのか。 unsigned と signed のアセンブラを比べるとわかるのかな。