C言語の参考書に乗ってたりするけど、使いどき、必要な時、なぜいるのかが具体的に説明されていなくて、自身が当時もやもやしていた文法が実際どう使われていたりするのか等を書いときます.
私みたいな情弱初心者の方に役にたつ内容になっていると嬉しい…
とりあえず unionとifdefだけ.気づいたら他にも追加する.
1.union(共用体)
unionの使いどきは全く想像つかなかった.なんでわざわざ同じメモリを使ってデータを入れていくんだろ…って思っていました…
同じメモリを使うってことから想像つくかもしれませんが、メモリの無駄使いを防ぐために使います.やはり設定ファイルにデータを書き出したり、実際にプログラムを動かしているときに大量のメモリを使うわけにはいきません.どんな性能のマシンで使われるか分からないのでできる限りメモリは使わないようにします.
じゃ、具体的にどんなときに同じメモリにデータを入れることになるんだよというのが次の疑問かと思います.
例えばいろんな種類の図形の情報を持つ可能性があるけど、一度には一つの図形の種類しか使わないとします.具体的には四角形、円、円弧、三角形、直線と言ったような図形から一つだけ形を選べてその形状情報をもっておきたいとします.四角形だったら二つの辺の長さ、丸なら半径、直線なら一つの長さ、三角形なら3辺の長さ、円弧なら角度と内径、外形などが必要になります.
そんなとき、図形の種類をzukei_typeとして保持するとして、他の各図形のパラメータをどう扱うべきでしょうか.
unionを使用しない場合
int zukei_type;
int shikaku_length_1;
int shikaku_length_2;
int circle_radius;
int line_length;
int trinangle_length_1;
…..
もしunionを使わないで変数を全て用意するならば上記のようになります. このときzukei_typeが”四角形”なら使うのは全部の変数のうちたった2つだけです.あとの変数は用意してあるけど絶対に0になります…そして使いません.全て無駄な変数になってしまいます.
そこで
unionを使えば(各図形の変数は構造体にしてまとめています.SHIKAKU_STRUCTには先ほどのlength_1とlength_2が入っています.)
int zukei_type;
union{
SHIKAKU_STRUCT shikaku;
CIRCLE_STRUCT circle;
…
}params;
上記のようにzukei_typeは同じですが、unionを使っているparamsの中には、複数の構造体が同じメモリを使っています.先ほどの例では、おそらく三角形の構造体または円弧の構造体が3つのintを使う一番大きい構造体だと思いますのでunionの大きさも一番大きい3つぶんのintになるかと思います.
これでメモリを無駄使いせずにすみます.またデータをひとまとめにできるので、複雑になりません.必要な時にどの形で取り出すかはzukei_typeを見ればよいからです.
2. ifdef
ifdef… いや、なんとなく使いどきはわかるけど、そんなにプリコンパイルでやる必要ある?って思っていました.if分で定義できそうなこと多いし.なぜifdefが用意されているのかわかっていませんでした.
一番わかりやすい例は、複数のOSやデバイスで動くような対応をしている場合かと思います.
慣れたら当たり前なのですが、初学者がC言語を習い始めた時にはあまり出くわさないため、イメージしにくいかと思います.
例えば、WindowsでもMacでも異なるOSで同じようなプログラムを使いたいことがあります.
でもWindowsから呼び出せるWindows専用の関数(API)やMacから呼び出せる関数(API)も同じような機能だとしても関数の名前も引数も全く異なります.
なるべくプログラムを変えず一つのプログラムに上記の異なる関数をWindowsではWindows用をMacではMac用を呼び出したい!
そんなときに使うのがifdefになります.
WindowsのためのプログラムをビルドするときにWINDOWSがdefine(定義)されているとして、MACのためのプルグラムをビルドする時にはMACがdefineされているものとすると
共通の関数A();
#ifdef WINDOWS
WINDOWSにしかない関数X();
#elif defined MAC
MACにしかない関数Y();
#endif共通の関数Z();
のように書くことができます.
WINDOWSが定義されていたら、プリコンパイルでMACの関数部分が削除されて、MACが定義されていれば、WINDOWSの関数の部分が削除されます.
WINDOWSの場合、プリコンパイル後には
共通の関数A();
WINDOWSにしかない関数X();
共通の関数Z();
MACの場合、プリコンパイル後には
共通の関数A();
MACにしかない関数Y();
共通の関数Z();
になってコンパイルが行われます.
定義されていない関数があるとビルドエラーが出てコンパイルができないために、上記のようにプリコンパイルでそれらのコードが削除されなくてはなりません.
そのような処理は通常のif文などではできないため、このようなことが行われます.