现在的位置: 首页 > 综合 > 正文

C/C++联合(Union)浅谈 .

2013年03月09日 ⁄ 综合 ⁄ 共 3640字 ⁄ 字号 评论关闭

联合提供了一种方式,能够规避C的类型系统,允许以多种类型来引用一个对象。联合声明的语法和结构体的语法一样,只不过语义相差很大。它们不是用不同的域来引用不同的存储器块,而是引用同一块存储块。

下面我们来举几个例子:

struct STest

{

       char   c;

       int    i[ 2 ];

       double var;

};

 

union UTest

{

       char   c;

       int    i[ 2 ];

       double var;

};

 

我们可以查看内存里面的分布:

类型         c     i    var    大小        

STest     0     4    12     20

UTest     0     0    0      8

上面的数据表示距离首地址的存储器块偏移。假如我们定义了UTest* pU; 我们分别察看p->c;  p->i[ 0 ];  p->var; 它们所引用的都是数据结构的起始位置。当然求sizeof的话。UTest的大小将是它的最大类型的数据成员的大小。

 

联合的用处很多,这里举一个怎么用它来节省空间:

假设我们有一个二叉树的数据结构,每个叶子节点都有一个double的数据值,而每个内部节点都有指向孩子节点的指针,但是没有数据(因为是叶子节点)。如果我们像这样声明:

struct NODE

{

       struct NODE* pLeft;

       struct NODE* pRight;

       double       data;

};

我们可以知道这样一个结构体需要16个字节,每个叶子节点都会浪费一半的字节。相反,如果我们用联合来声明一个节点:

union NODE

{

       struct

       {

              union NODE* pLeft;

              union NODE* pRight;

       }inter;

       double data;

};

这样一来,每个节点就只需要8个字节。如果pNode是一个指向union NODE类型的指针,我们用pNode->data来引用叶子节点的数据。而pNode->inter.pLeft和pNode->inter.pRight来引用内部节点的孩子。

这样可能出现一种情况,就是无法确定是哪种节点(内部节点或叶子节点)。我们可以引用一个标志域。

struct NODE

{

       BOOL  isLeaf;

       union

       {

              struct

              {

                     union NODE* pLeft;

                     union NODE* pRight;

              }inter;

              double data;

       }info;

}

 

不过对于这样小的节省而导致代码的可读性变得差了一些。在这里联合带来的好处可以忽略。对于较多域的数据结构,这样的节省会更加吸引人一些。

 

还有一个用法就是用来访问不同数据类型的位。如:

UINT floatToBits( float fVar )

{

       union

       {

               float  fV;

               UINT uI;

       }temp;

       temp.fV = fVar;

       return temp.uI;

}

我们看看汇编代码:

mov         eax,dword ptr [ fVar ]

mov         dword ptr [ temp ],eax

 

它跟下面的函数产生回汇编代码是一样的:

UINT floatToBits( UINT var )

{

       return var;

}

这就证明汇编代码里面缺乏信息,无论是什么类型都相对于EBP偏移固定的值。过程只是简单的拷贝,并没有修改任何位。

 

再举个例子吧:

double bitToDouble( UINT uParam1, UINT uParam2 )

{

       union

       {

               double d;

               UINT u[ 2 ];  

       }temp;

       temp.u[ 0 ] = uParam1;

       temp.u[ 1 ] = uParam2;

       return temp.d;

}

 

好了,更多的用法大家在慢慢体会吧,这里就抛砖引玉了- -

联合提供了一种方式,能够规避C的类型系统,允许以多种类型来引用一个对象。联合声明的语法和结构体的语法一样,只不过语义相差很大。它们不是用不同的域来引用不同的存储器块,而是引用同一块存储块。

下面我们来举几个例子:

struct STest

{

       char   c;

       int    i[ 2 ];

       double var;

};

 

union UTest

{

       char   c;

       int    i[ 2 ];

       double var;

};

 

我们可以查看内存里面的分布:

类型         c     i    var    大小        

STest     0     4    12     20

UTest     0     0    0      8

上面的数据表示距离首地址的存储器块偏移。假如我们定义了UTest* pU; 我们分别察看p->c;  p->i[ 0 ];  p->var; 它们所引用的都是数据结构的起始位置。当然求sizeof的话。UTest的大小将是它的最大类型的数据成员的大小。

 

联合的用处很多,这里举一个怎么用它来节省空间:

假设我们有一个二叉树的数据结构,每个叶子节点都有一个double的数据值,而每个内部节点都有指向孩子节点的指针,但是没有数据(因为是叶子节点)。如果我们像这样声明:

struct NODE

{

       struct NODE* pLeft;

       struct NODE* pRight;

       double       data;

};

我们可以知道这样一个结构体需要16个字节,每个叶子节点都会浪费一半的字节。相反,如果我们用联合来声明一个节点:

union NODE

{

       struct

       {

              union NODE* pLeft;

              union NODE* pRight;

       }inter;

       double data;

};

这样一来,每个节点就只需要8个字节。如果pNode是一个指向union NODE类型的指针,我们用pNode->data来引用叶子节点的数据。而pNode->inter.pLeft和pNode->inter.pRight来引用内部节点的孩子。

这样可能出现一种情况,就是无法确定是哪种节点(内部节点或叶子节点)。我们可以引用一个标志域。

struct NODE

{

       BOOL  isLeaf;

       union

       {

              struct

              {

                     union NODE* pLeft;

                     union NODE* pRight;

              }inter;

              double data;

       }info;

}

 

不过对于这样小的节省而导致代码的可读性变得差了一些。在这里联合带来的好处可以忽略。对于较多域的数据结构,这样的节省会更加吸引人一些。

 

还有一个用法就是用来访问不同数据类型的位。如:

UINT floatToBits( float fVar )

{

       union

       {

               float  fV;

               UINT uI;

       }temp;

       temp.fV = fVar;

       return temp.uI;

}

我们看看汇编代码:

mov         eax,dword ptr [ fVar ]

mov         dword ptr [ temp ],eax

 

它跟下面的函数产生回汇编代码是一样的:

UINT floatToBits( UINT var )

{

       return var;

}

这就证明汇编代码里面缺乏信息,无论是什么类型都相对于EBP偏移固定的值。过程只是简单的拷贝,并没有修改任何位。

 

再举个例子吧:

double bitToDouble( UINT uParam1, UINT uParam2 )

{

       union

       {

               double d;

               UINT u[ 2 ];  

       }temp;

       temp.u[ 0 ] = uParam1;

       temp.u[ 1 ] = uParam2;

       return temp.d;

}

 

好了,更多的用法大家在慢慢体会吧,这里就抛砖引玉了- -

【上篇】
【下篇】

抱歉!评论已关闭.