C言語プログラミングにおける連合体union入門学習チュートリアル

6775 ワード

ユニオン(union)はC言語で特殊なデータ型であり、異なるタイプのデータを同じメモリ位置に格納することができる.複数のメンバーを使用する連合体を定義できますが、1つのコンポーネントだけがいつでも指定された値を含むことができます.共同体は、同じメモリ位置を用いて多用途に有効な方法を提供する.
連合体を定義連合体を定義するには、union文を使用して構造を定義するのと似ている必要があります.連合体宣言には、プログラムが1つ以上のメンバーを持つ新しいデータ型が定義されています.連合体宣言の形式は次のとおりです.

union [union tag]
{
  member definition;
  member definition;
  ...
  member definition;
} [one or more union variables]; 

Unionラベルはオプションで、各メンバーの定義はint iなどの正常な変数定義です.およびfloat f;または他の有効な変数の定義.連合体定義の末尾、最後のセミコロンの前に、1つ以上の変数の連係を指定できますが、オプションです.ここでは、データ・コンビネーション・タイプという3つのメンバーi,f,strを定義します.

union Data
{
  int i;
  float f;
  char str[20];
} data; 

データ型の変数に格納できる整数、浮動小数点数、または文字列.これは、1つの単一の可変構造、すなわち、同じ記憶ユニットが、複数のタイプのデータを記憶するために使用できることを意味する.任意の組み込みまたはユーザー定義のデータ型を使用して、必要に応じて統合できます.
unionによって消費されるメモリは、コンビネーションの最大メンバーを収容するのに十分な大きさになります.例えば、上記の例のデータ型は、文字列によって占有される最大の空間であるため、20バイトの記憶領域を占有する.以下に、上述した統合型のメモリサイズの例を示します.

#include 
#include 
 
union Data
{
  int i;
  float f;
  char str[20];
};
 
int main( )
{
  union Data data;    

  printf( "Memory size occupied by data : %d
", sizeof(data));

  return 0;
}


上記のプログラムをコンパイルして実行します.これにより、次の結果が得られます.

Memory size occupied by data : 20

連合体メンバーにアクセス連合体の任意のメンバーにアクセスするには、メンバーアクセス演算子(.)を使用します.メンバー・アクセス演算子は、連合体変数名とメンバーとして符号化され、unionキーを使用して連合体タイプの変数を定義します.以下に、連合体の使用例を示します.

#include 
#include 
 
union Data
{
  int i;
  float f;
  char str[20];
};
 
int main( )
{
  union Data data;    

  data.i = 10;
  data.f = 220.5;
  strcpy( data.str, "C Programming");

  printf( "data.i : %d
", data.i);
  printf( "data.f : %f
", data.f);
  printf( "data.str : %s
", data.str);

  return 0;
}


上記のプログラムをコンパイルして実行します.これにより、次の結果が得られます.

data.i : 1917853763
data.f : 4122360580327794860452759994368.000000
data.str : C Programming

ここで、strメンバーの値が良好に印刷された場合、変数の最終値が占有されたメモリ位置に割り当てられたため、連合体メンバーiとfの値が破損していることがわかります.次に、同じ例をもう一度見てみましょう.同じ時間に変数を使用します.これは連合体の主な目的です.

#include 
#include 
 
union Data
{
  int i;
  float f;
  char str[20];
};
 
int main( )
{
  union Data data;    

  data.i = 10;
  printf( "data.i : %d
", data.i);
  
  data.f = 220.5;
  printf( "data.f : %f
", data.f);
  
  strcpy( data.str, "C Programming");
  printf( "data.str : %s
", data.str);

  return 0;
}


上記のプログラムをコンパイルして実行します.これにより、次の結果が得られます.

data.i : 10
data.f : 220.500000
data.str : C Programming

ここでは、1つの部品が一度に使用されるため、すべてのメンバーが非常によく印刷されています.
アプリケーションの場合、複数のデータを共有する必要がある場合や、複数のデータを毎回一時的に取得する必要がある場合は、コンビネーション(union)を利用することができます.C Programming Languageという本では、連合体についてこう述べています.
     1)連合体は構造である;
     2)そのすべてのメンバーのベースアドレスに対するオフセット量は0である.
     3)この構造空間は、最も「幅の広い」メンバーを収容するのに十分な大きさである.
     4)すべてのメンバーに合わせるように配置されます.
次の4つの説明を説明します.
     コンビネーション内のすべてのメンバーはメモリを共有しているため、各メンバーの格納ヘッダアドレスは、コンビネーション変数のベースアドレスに対するオフセット量が0であり、すなわち、すべてのメンバーのヘッダアドレスが同じである.すべてのメンバーがメモリを共有できるようにするには、これらのメンバーの中で最も広いメンバーを収容するのに十分なスペースが必要です.この「整列はすべてのメンバーに適合する」とは、すべてのメンバーの自己整列に適合する必要があることを意味します.
次に例を示します.
例えば連合体

union U
{
  char s[9];
  int n;
  double d;
};

sは9バイト、nは4バイト、dは8バイトであるため、少なくとも9バイトの空間が必要である.しかし、実際のサイズは9ではなく、演算子sizeofでそのサイズが16であることをテストしたのは、ここでバイト整列の問題があり、9は4でも8でも整列できないためである.したがって、バイトは16に追加され、すべてのメンバーの自己整列に合致します.ここから、連合体が占める空間は、最も広いメンバーに依存するだけでなく、すべてのメンバーと関係があることがわかります.すなわち、その大きさは2つの条件を満たす必要があります.1)最も広いメンバーを収容するのに十分な大きさです.2)サイズは、含まれるすべての基本データ型のサイズによって割り切れる.
試験手順:

/*      2011.10.3*/

#include 
using namespace std;

union U1
{
  char s[9];
  int n;
  double d;
};

union U2
{
  char s[5];
  int n;
  double d;
};

int main(int argc, char *argv[])
{
  U1 u1;
  U2 u2;
  printf("%d
",sizeof(u1)); printf("%d
",sizeof(u2)); printf("0x%x
",&u1); printf("0x%x
",&u1.s); printf("0x%x
",&u1.n); printf("0x%x
",&u1.d); u1.n=1; printf("%d
",u1.s[0]); printf("%lf
",u1.d); unsigned char *p=(unsigned char *)&u1; printf("%d
",*p); printf("%d
",*(p+1)); printf("%d
",*(p+2)); printf("%d
",*(p+3)); printf("%d
",*(p+4)); printf("%d
",*(p+5)); printf("%d
",*(p+6)); printf("%d
",*(p+7)); return 0; }

出力結果:

16
8
0x22ff60
0x22ff60
0x22ff60
0x22ff60
1
0.000000
1
0
0
0
48
204
64
0
       . . .


sizeof(u 1)=16の場合.u 1ではsが9バイト,nが4バイト,dが8バイトであるため,少なくとも9バイトが必要である.基本データ型はchar,int,doubleがそれぞれ1,4,8バイトを占め,u 1が占める空間の大きさを1,4,8で割り切るためには16バイトを埋める必要があるためsizeof(u 1)=16.
sizeof(u 2)=8の場合.u 2ではsが5バイト,nが4バイト,dが8バイトであるため,少なくとも8バイトが必要である.基本データ型はchar,int,doubleがそれぞれ1,4,8バイトを占め,u 2が占める空間の大きさを1,4,8で割り切るためにバイトを埋め込む必要はなく,8自体が要求を満たすためである.したがってsizeof(u 2)=8である.
印刷された各メンバーのベースアドレスから、連合体内の各メンバーのベースアドレスは、連合体変数の最初のアドレスと同じであることがわかります.
u 1.n=1に対して、u 1のnを1に割り当てた場合、このセグメントのメモリの最初の4バイトに格納されるデータは、1億1,000,000,000,000,000,000,000,000,000,000,000になります.
したがって、s[0]をとるデータは、1番目のセルをとるデータであり、その整数値が1であるため、印刷結果は1である.
印刷されたdが0.000000である場合、以下のようにしたい.このセグメントのメモリの上位4バイトのセルに格納されているデータは、1億1,000,000,000,000,000,000,000,000であることが知られているため、上からの印刷結果は48204,64,0であり、後の4バイトのセルのデータは、0,10000,11001100,0100億,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000,000
00000000 01000000 11001100 00110000 00000000 00000000 00000000 00000001
double型データの場合、63ビット目は0が符号ビット、62-52億0100が次数符号、11001100億10000000000億円10000000000億円10000000000億円が端数となり、その値から端数値は約0、次数符号は4-1023=-1019となっているため、浮動小数点数は1.0*2^(-1019)=0.00000000000億円…となり、出力結果は0.000000となった.