• ベストアンサー

共用体について

"union"についての質問です。 共用体を使うメリット…と言うか、 具体的にはどういう時に使い道があるのでしょうか。 どこかの本にはメモリが節約できるよ!といったことが書いてありました。 が、 メモリが共用だからメンバ1つ書き換えると他のメンバの内容も破壊されるし、いまいち共用体のメリットが見出せません。 どなたか、できれば具体例と一緒に共用体を使いどころをご教授していただければ嬉しいです。

質問者が選んだベストアンサー

  • ベストアンサー
  • jacta
  • ベストアンサー率26% (845/3158)
回答No.1

いろいろありますが、一例として... union xxx {  unsigned char d[0x10000];  long long force_aligner; }; のようにすれば、配列dを強制的にlong longと同じ境界調整を行うことができます。 典型的に使い方としては、 union yyy {  uint8_t b[2];  uint16_t w; }; のような感じで、16ビット整数としても、それを上下に分割した8ビット整数としても使えるような型を定義するのがあります。 ただし、バイトーオーダーが処理系に依存するので、濫用するのは関心できません。 同様に、ビットフィールドを組み合わせるやりかたもあります。これもビットオーダーが処理系に依存しますので要注意です。

htk433
質問者

お礼

回答ありがとうございます。 ・1つ目について 境界調整(アラインメント)についての理解が完全ではないのであまり自信はありませんが、もし違っていたらご指摘願います;; 共用体のメンバにlong long型の(=どのデータ型にも適合できるような)アラインメント用ダミー変数を挿入することによって、必ず&d[0]が8の倍数アドレスに割り当てられる、ということでよろしいですかね。 こうすることで冗長(?)なメモリアクセスをなくせる・・・と、解釈しておりますが。 ・2つ目について No.2、No.3の方が言っておられるテクニックですね。 データを(例えば)上位と下位で分けて扱いたい場合に共同体を使うことでお手軽に扱える、と。 組み込みでよく見そうなunionの活用法ですね。

その他の回答 (8)

  • Wr5
  • ベストアンサー率53% (2173/4061)
回答No.9

Wndows Mobileで以下のようなものがあります。 typedef union CEVALUNION { short iVal; USHORT uiVal; long lVal; ULONG ulVal; FILETIME filetime; LPWSTR lpwstr; CEBLOB blob; BOOL boolVal; double dblVal; } CEVALUNION; typedef struct CEPROPVAL { CEPROPID propid; WORD wLenData; WORD wFlags; CEVALUNION val; } CEPROPVAL; データの受け渡しはstruct CEPROPVALで。 共用体valに入っているデータのタイプはpropidで区別。という具合です。 最近関わっているプロジェクトでは、組み込み系でメモリ容量に制限があって、各通信データごとの構造体を個別に持てないので、 共用体にして1つの大きく確保した領域(グローバル変数)を使い回しています。 # 勿論、途中で別のデータを入れたりしたら元のものは壊れるコトになりますが…。 # そうならないように制御しています。

回答No.8

個人的に固定長データを扱うときによく利用しています。 NUL終端しないのでマクロなり専用関数とセットですが。。。 union { char *stream; struct { char field1[1]; char field2[5]; char field3[10]; } f; } in_data; としておいて、読み取り/書き込みは fgets(in_data.stream, sizeof(in_data.f), fp) みたいな感じ 使うときは printf("%.*s", sizeof(in_data.f.field2), in_data.f.field2); まぁ、直接構造体を (char *)しちゃってもいいんですけどね キャストはあまり好きじゃないんで。。。 あと、トリッキーですが上の応用で 文字列を連結できたり/可変長レコードを処理できますね

  • rays1103
  • ベストアンサー率39% (13/33)
回答No.7

私自身は共用体を使うことはほとんどありませんが、 ネットワーク系のプログラムでIPヘッダの構造体に使用しているのを見たことがあります。 IPヘッダではそれぞれのデータが4ビットであったり32ビットであったりとデータのビット長がバラバラですが、 チェックサムを計算する際には同じビット長でアクセスできた方が都合が良いため、 通常のデータ参照用の変数と、チェックサムを計算するための固定長の配列の変数とを共用体にして、 それぞれの用途で使用していました。

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.6

> ・例外的な存在というのは上記でURLで言う「アラインメントに寛容なCPU」を指すものであって、アラインメントに厳格なCPUではご指摘の通りアラインメントが正しく行われていない場合の動作は未定義→エラー。 未定義というのはエラーになるかどうかもわかりません。 例えば、あるCPUでは、奇数番地にワードアクセスしようとすると、勝ってにアドレスの下位ビットを0に置き換えてしまったりします。つまり、期待していないアドレスへのアクセスになってしまいます。 これは、一見動作するように見えるので、深刻なバグにつながります。 > ・(私の使っているパソコンのCPUは寛容な方だったので)アラインメントに特に気を配らなくてもアクセスは行える→アクセスが遅くなるだけで済む。 > なので冗長なメモリアクセスと解釈したが、実際は他のCPUでは動かないことの方が多い。 8ビットCPUの場合は確実に動きます。 16ビットCPUになると動くもの、動かないものが出てきます。 32ビット以上の場合、8ビットや16ビットCPUの上位互換として発展してきたものを除けば、まともに動かないのが普通です。

  • jacta
  • ベストアンサー率26% (845/3158)
回答No.5

#1 です。 > 共用体のメンバにlong long型の(=どのデータ型にも適合できるような)アラインメント用ダミー変数を挿入することによって、必ず&d[0]が8の倍数アドレスに割り当てられる、ということでよろしいですかね。 8の倍数かどうかは処理系によりますが、まあそういうことです。 > こうすることで冗長(?)なメモリアクセスをなくせる・・・と、解釈しておりますが。 こんなことをするのは、多くの場合、ハード的な制約からです。 ですので、冗長なメモリアクセスというよりは、こうしていなければ動かないことが多いといえます。 あるいは、mallocのようなメモリアロケータを自作する場合にもこういった手法を使うことがあります。 その場合、配列の要素へのアクセスは、unsigned char以外の型のポインタを介して行われることがあります。 境界調整が正しく行われていない場合の間接参照は未定義の動作ですので、CPU例外が発生したり期待しないアドレスにアクセスすることがあります(IA-32などはどちらかといえば例外的な存在です)。

htk433
質問者

お礼

ありがとうございます。 度々申し訳ないのですが、自分の解釈を以下に書いてみますので誤りなどがあればご指摘お願いします。 ※ご指摘を理解するにあたって、http://www5d.biglobe.ne.jp/~noocyte/Programming/Alignment.html を参考にしました。 ・例外的な存在というのは上記でURLで言う「アラインメントに寛容なCPU」を指すものであって、アラインメントに厳格なCPUではご指摘の通りアラインメントが正しく行われていない場合の動作は未定義→エラー。 ・(私の使っているパソコンのCPUは寛容な方だったので)アラインメントに特に気を配らなくてもアクセスは行える→アクセスが遅くなるだけで済む。 なので冗長なメモリアクセスと解釈したが、実際は他のCPUでは動かないことの方が多い。

  • titokani
  • ベストアンサー率19% (341/1726)
回答No.4

パケットを表す構造体とかで、 struct A{ .... }; struct B{ .... }; struct C{ .... }; enum TYPE{TYPE_A,TYPE_B,TYPE_C}; struct packet { TYPE type; union DATA{ A a; B b; C c; }data; }; なんてのが時々ありますね。

htk433
質問者

お礼

ありがとうございます。 私はその表現は見たことがないのですが、パケットの種類が違っても同じデータ構造で扱える、という感じの使い方ですね。 送受信の対象となるデータ構造を1つにする→プログラムが組みやすい(?)・・・という感じでしょうか。

noname#106310
noname#106310
回答No.3

C のバイブル(K&R)の利用例では迫力がありませんが,マイクロプロセッサのシミュレーションでレジスタペア(※)の表現に重宝だと思いました. ※ i8086 の AX=(AH, AL) は AH, AL を独立に使うことも AX として使うこともできます.最近の CPU のアーキテクチャは知りませんが,同様ではないでしょうか(無責任推測).

htk433
質問者

お礼

回答ありがとうございます。 そういえば以前私は組み込みをやっていたんですが、 レジスタ定義ファイルに共同体がバリバリ使われていました。レジスタそのものと、中身のビットを共同体で定義することでどちらでも扱えますよ、と。 初めて見た時には「うまいなぁ…」と思いました。似たような例として思い出したので書かせてもらいました(笑)

回答No.2

苗字はなんていうんですか? 東京です 下のお名前は?       ばな奈です 貴方のフルネームは?    東京ばな奈です こんなとき貴方は”苗字”、”下の名前”、”氏名”って持ちますか? それとも苗字と下の名前を合わせたのが氏名だって考えますか? こんな例っていっぱいありますよね。 住所だって、その中には都道府県も含んでるし、市区町村も含んでます。 住所と都道府県、市区町村を別々に持ちますか? 住所が”東京都千代田区丸の内XXX”ってなってるのに都道府県が”大阪”だったら「どうすんねん!」です。 いちいち2回も3回も書くの面倒だし、書き間違えたら困りますからね。

htk433
質問者

お礼

回答ありがとうございます。 データをバラで扱いたい場合があるときは共同体のメンバとして定義しておくと楽、ということですね。

関連するQ&A