C/C++联合(Union)浅谈
2016-01-25 16:37
393 查看
原文:http://blog.csdn.net/masefee/article/details/4160211
联合提供了一种方式,能够规避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;
}
好了,更多的用法大家在慢慢体会吧,这里就抛砖引玉了- -
相关文章推荐
- C++技巧之名字空间namespace
- 在C++中调用DLL中的函数
- 在C++中调用DLL中的函数
- C++技巧之名字空间namespace
- C/C++联合(Union)浅谈
- C++拷贝构造函数(深拷贝,浅拷贝)
- VC++数据类型
- C++ Template 中的typename、class关键字区别
- C++类模板的成员函数模板写法
- C++成员函数指针的应用
- C++中回调(CallBack)的使用方法
- 偷Microsoft师学MFC艺:且看C++如何支持反射
- 简单的C++委托 —— 用模板类实现类成员函数的回调
- C#, C++, Java性能对比
- C++ 程序员必读书目清单
- C#, C++, Java性能对比
- C++ 程序员必读书目清单
- C/C++文件IO输入输出操作——FILE*、fstream、windowsAPI
- 【转】基本JNI调用技术(c/c++与java互调)
- 【转】使用JNI进行混合编程:在C/C++中调用Java代码