C言語っていうのは、コンパイラの違いによる問題も多くある。
もちろん、標準仕様の範囲内での違いであることがほとんどであるけれど、それが大きな問題につながることもよくある。
今回は構造体のサイズの問題について。
構造体はたとえば
sruct foo{ int a; char b[2]; short c; };
のように宣言する。
このとき、たとえば
sizeof(foo)==sizeof(foo.a)+sizeof(foo.b)+sizeof(foo.c)
といった条件式があったとしよう。
この条件式はいつでも真となるだろうか。
知っている人は簡単にわかるような問題かもしれないが、この問題にしっかりとした正解を出すにはコンパイラレベルの知識が求められる。
正解から言えば、「コンパイラによって真にも偽にもなりえる」である。
なぜこのような問題が発生するのか。
答えは「アラインメント」にある。
CPUには扱いやすいデータサイズというものがあり、それに合わせるために上の構造体の例であれば、コンパイラによってはchar[2] b やshort c という2バイトのデータのすぐ後にメモリ上2バイトを追加して、4バイトずつメモリ上にデータが並んでいるような状態にすることがある。
そうすると条件式は、右辺は4+2+2=8となるのに左辺は4+4+4=12となることになる。
もちろんこれは偽となる。
このようにC言語では「高級言語」らしからぬ、「低級言語」的な問題を含むこともある。
この問題を回避するためには
1.コンパイラのアラインメントのスイッチを切り替える。
2.適切なパディングバイトを挿入する。
(※これを説明するべきところで、間違って別の説明と混同した内容が書かれていました。これが正しい説明です。)
などが考えられる。
2.はたとえば
struct foo{ int a; char b[2]; char pad1[2]; short c; char pad2[2]; };
として、パディングバイトを挿入することで、自分が理解しやすい状態にするのである。
もちろん、自分の使うコンパイラに対するしっかりとした理解があれば問題はないし、自分の作ったコードが自分の知らないコンパイラでコンパイルされる可能性があったとしても、しっかりとした知識と対処法があればそれこそ問題を未然に回避することは可能である。
C言語以外にももしかすれば、このような移植性の問題をはらむ言語も存在しているかもしれない。
今回はコンパイラの違いだったけれど、OSやプロセッサ、はたまた確保できるメモリ数の差といったものまで、プログラマを悩ませる移植性の問題は多く存在する。
ということを学んだ。
ただこういう問題は移植して初めて表面化する問題だからこそ難しい。
デバイスドライバとかの開発なども始めてみようと思っている身としては、こういう知識はしっかりと身につけておきたい。