最近のCコンパイラは整数拡張されるのが普通
高木です。おはようございます。
昨年の夏ごろからマイコン向けのコードを書く機会が結構ありました。
開発を始めたころは、久しぶりにCコンパイラと対峙するので、多少の不安もあり、また同時に楽しみでもありといった複雑な心境だったのを覚えています。
Cは最も好きな言語のひとつですし、最も得意な言語であるといってもよいでしょう。
久しぶりでしたが、忘れているどころか脳内では自然にCで思考している自分に気づきました。
そんなマイコンのCについて、昨日職場での話をきっかけに昔のコンパイラのことを思い出しました。
私が社会人になったのは1990年で、ちょうどその年、プログラム言語 Cの国際規格であるISO/IEC 9899:1990が発行されました。
当時の私はまだCは一度も触ったことがなく、何年か経ってようやく独学で身につけたことを覚えています。
最初に使ったCコンパイラはMS-DOS向けのMicrosoft C 6.0だったと思います。
Microsoft Cは現在の感覚からすればかなり癖があるコンパイラでしたが、それでも比較的「普通の」コンパイラでした。
その後、マイコン向けのCコンパイラに触れていくことになるわけですが、Microsoft Cに比べてもはるかに癖が強く、「普通ではない」コンパイラが多かったのを覚えています。
そんなマイコン向けのCコンパイラをいくつも触っていると、共通した癖があることに気づきます。
そのうちのひとつが、デフォルトでは汎整数拡張(integral promotion)されないというものです。
それもそのはずで、8ビットのマイコンや、それを拡張した16ビットのマイコンが主流でしたので、杓子定規に標準規格のとおり汎整数拡張していると効率が悪くてしかたがなかったのでしょう。
8ビットや16ビットのマイコンですから、int型のサイズは16ビットになります。
標準規格ではint型の表現範囲は-32767~+32767またはそれ以上でなければなりません。
いくら8ビットマイコンであっても16ビットはどうしても必要になるのです。
しかし、8ビットマイコンではレジスタ(とくにアキュムレータ)が8ビット幅しかないことも普通にあり、単にメモリからレジスタに値を読み込むだけでも16ビットをそのまま使えないこともあったのです。
そんな状況ですから、8ビットの値に対して符号拡張が必要になる汎整数拡張は非常にコストがかかりました。
算術演算や論理演算を行う場合でもそうです。
8ビットのアキュムレータしかない状況で16ビットの加減算を行おうとすると、まずは下位バイトを計算し、その際の繰り上がりを考慮しながら上位バイトを計算する必要があります。
ちょうど筆算で足し算や引き算をやるときと同じ要領です。
乗除算器がないことが普通でしたので、掛け算や割り算を行おうとすると自分でサブルーチンを書かないといけません。
8ビットどうしの掛け算や割り算でも面倒なのに、16ビットだとはるかに面倒になります。
こう考えると、デフォルトでは汎整数拡張を行わないというコンパイラの設計には合理性があります。
たとえ標準規格に準拠することを捨てても、それに見合うだけのメリットがあったのです。
汎整数拡張とはちょっと違いますが、マイコン向けのCコンパイラの中には、sizeof演算子の評価結果の型が状況によって変わるものまであったように思います。
ローエンドのマイコンでは、大多数のオブジェクトは256バイト未満ですので、そのような状況ではsizeof演算子の評価結果をunsigned char型にしていたのです。
もちろん標準規格には準拠しませんし、移植性には大きな問題となりますが、やはり効率優先だったのでしょう。
そんな昔から20年ぐらいたった現在、扱っているマイコンも32ビットになりました。
Cで開発することが前提となっていることもあって、マイコンのアーキテクチャもCとの親和性が高いものになっています。
汎用レジスタのサイズも32ビットありますし、符号拡張やゼロ拡張しやすいための命令も備わっています。
もちろん、32ビット単位での足し算や引き算もできますし、掛け算や割り算も簡単にできるようになりました。
標準規格もバージョンアップしていますので、マイコンでもC99が使えます(最新規格はC11ですが……)。
もうデフォルトで汎整数拡張が行われないなどということはありません。
正確にいうと、C99ですから汎整数拡張ではなく「整数拡張(integer promotion)」ですね。
標準規格ができる前の黎明期から、Cでは汎整数拡張が行われるのが「普通」でした。
マイコン向けのコンパイラが「普通」ではなかったのです。
けれども、今ではマイコン向けでも汎整数拡張が行われるのが普通になったように思います。
ちなみに現在でも8ビットのマイコンはあります。
ArduinoでおなじみのAVRなんかも8ビットのマイコンです。
けれども、ArduinoではGCCを使っていることもあり、普通に汎整数拡張が行われます。
同じ8ビットのマイコンでもずいぶん進化したものです。