見出し画像

【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 の場合・・・

次のような記述があります。

Which of signed char or unsigned char has the same range, representation, and behavior as “plain” char (C90 6.1.2.5, C90 6.2.1.1, C99 and C11 6.2.5, C99 and C11 6.3.1.1).
Determined by ABI. The options -funsigned-char and -fsigned-char change the default. See Options Controlling C Dialect.

gcc.gnu org

『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 のアセンブラを比べるとわかるのかな。


この記事が参加している募集

この経験に学べ

この記事が気に入ったらサポートをしてみませんか?