アプリ版:「スタンプのみでお礼する」機能のリリースについて

"union"についての質問です。

共用体を使うメリット…と言うか、
具体的にはどういう時に使い道があるのでしょうか。

どこかの本にはメモリが節約できるよ!といったことが書いてありました。

が、
メモリが共用だからメンバ1つ書き換えると他のメンバの内容も破壊されるし、いまいち共用体のメリットが見出せません。

どなたか、できれば具体例と一緒に共用体を使いどころをご教授していただければ嬉しいです。

A 回答 (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つの大きく確保した領域(グローバル変数)を使い回しています。
# 勿論、途中で別のデータを入れたりしたら元のものは壊れるコトになりますが…。
# そうならないように制御しています。
    • good
    • 0

個人的に固定長データを扱うときによく利用しています。


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 *)しちゃってもいいんですけどね
キャストはあまり好きじゃないんで。。。

あと、トリッキーですが上の応用で
文字列を連結できたり/可変長レコードを処理できますね
    • good
    • 1

私自身は共用体を使うことはほとんどありませんが、


ネットワーク系のプログラムでIPヘッダの構造体に使用しているのを見たことがあります。

IPヘッダではそれぞれのデータが4ビットであったり32ビットであったりとデータのビット長がバラバラですが、
チェックサムを計算する際には同じビット長でアクセスできた方が都合が良いため、
通常のデータ参照用の変数と、チェックサムを計算するための固定長の配列の変数とを共用体にして、
それぞれの用途で使用していました。
    • good
    • 0

> ・例外的な存在というのは上記でURLで言う「アラインメントに寛容なCPU」を指すものであって、アラインメントに厳格なCPUではご指摘の通りアラインメントが正しく行われていない場合の動作は未定義→エラー。



未定義というのはエラーになるかどうかもわかりません。
例えば、あるCPUでは、奇数番地にワードアクセスしようとすると、勝ってにアドレスの下位ビットを0に置き換えてしまったりします。つまり、期待していないアドレスへのアクセスになってしまいます。
これは、一見動作するように見えるので、深刻なバグにつながります。

> ・(私の使っているパソコンのCPUは寛容な方だったので)アラインメントに特に気を配らなくてもアクセスは行える→アクセスが遅くなるだけで済む。
> なので冗長なメモリアクセスと解釈したが、実際は他のCPUでは動かないことの方が多い。

8ビットCPUの場合は確実に動きます。
16ビットCPUになると動くもの、動かないものが出てきます。
32ビット以上の場合、8ビットや16ビットCPUの上位互換として発展してきたものを除けば、まともに動かないのが普通です。
    • good
    • 0

#1 です。



> 共用体のメンバにlong long型の(=どのデータ型にも適合できるような)アラインメント用ダミー変数を挿入することによって、必ず&d[0]が8の倍数アドレスに割り当てられる、ということでよろしいですかね。

8の倍数かどうかは処理系によりますが、まあそういうことです。

> こうすることで冗長(?)なメモリアクセスをなくせる・・・と、解釈しておりますが。

こんなことをするのは、多くの場合、ハード的な制約からです。
ですので、冗長なメモリアクセスというよりは、こうしていなければ動かないことが多いといえます。

あるいは、mallocのようなメモリアロケータを自作する場合にもこういった手法を使うことがあります。
その場合、配列の要素へのアクセスは、unsigned char以外の型のポインタを介して行われることがあります。
境界調整が正しく行われていない場合の間接参照は未定義の動作ですので、CPU例外が発生したり期待しないアドレスにアクセスすることがあります(IA-32などはどちらかといえば例外的な存在です)。
    • good
    • 0
この回答へのお礼

ありがとうございます。

度々申し訳ないのですが、自分の解釈を以下に書いてみますので誤りなどがあればご指摘お願いします。
※ご指摘を理解するにあたって、http://www5d.biglobe.ne.jp/~noocyte/Programming/ … を参考にしました。

・例外的な存在というのは上記でURLで言う「アラインメントに寛容なCPU」を指すものであって、アラインメントに厳格なCPUではご指摘の通りアラインメントが正しく行われていない場合の動作は未定義→エラー。
・(私の使っているパソコンのCPUは寛容な方だったので)アラインメントに特に気を配らなくてもアクセスは行える→アクセスが遅くなるだけで済む。
なので冗長なメモリアクセスと解釈したが、実際は他のCPUでは動かないことの方が多い。

お礼日時:2010/03/03 16:42

パケットを表す構造体とかで、


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;
};
なんてのが時々ありますね。
    • good
    • 0
この回答へのお礼

ありがとうございます。

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

お礼日時:2010/03/03 16:00

C のバイブル(K&R)の利用例では迫力がありませんが,マイクロプロセッサのシミュレーションでレジスタペア(※)の表現に重宝だと思いました.


※ i8086 の AX=(AH, AL) は AH, AL を独立に使うことも AX として使うこともできます.最近の CPU のアーキテクチャは知りませんが,同様ではないでしょうか(無責任推測).
    • good
    • 0
この回答へのお礼

回答ありがとうございます。

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

お礼日時:2010/03/03 01:36

苗字はなんていうんですか? 東京です


下のお名前は?       ばな奈です
貴方のフルネームは?    東京ばな奈です

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

回答ありがとうございます。

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

お礼日時:2010/03/03 01:26

いろいろありますが、一例として...



union xxx
{
 unsigned char d[0x10000];
 long long force_aligner;
};

のようにすれば、配列dを強制的にlong longと同じ境界調整を行うことができます。

典型的に使い方としては、

union yyy
{
 uint8_t b[2];
 uint16_t w;
};

のような感じで、16ビット整数としても、それを上下に分割した8ビット整数としても使えるような型を定義するのがあります。
ただし、バイトーオーダーが処理系に依存するので、濫用するのは関心できません。
同様に、ビットフィールドを組み合わせるやりかたもあります。これもビットオーダーが処理系に依存しますので要注意です。
    • good
    • 0
この回答へのお礼

回答ありがとうございます。

・1つ目について
境界調整(アラインメント)についての理解が完全ではないのであまり自信はありませんが、もし違っていたらご指摘願います;;
共用体のメンバにlong long型の(=どのデータ型にも適合できるような)アラインメント用ダミー変数を挿入することによって、必ず&d[0]が8の倍数アドレスに割り当てられる、ということでよろしいですかね。
こうすることで冗長(?)なメモリアクセスをなくせる・・・と、解釈しておりますが。

・2つ目について
No.2、No.3の方が言っておられるテクニックですね。
データを(例えば)上位と下位で分けて扱いたい場合に共同体を使うことでお手軽に扱える、と。
組み込みでよく見そうなunionの活用法ですね。

お礼日時:2010/03/03 01:23

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!