yyyyyyyyyyyyyyyyyyyy

博客园 首页 新随笔 联系 订阅 管理
union 关键字的用法与struct 的用法非常类似。

union 维护足够的空间来置放多个数据成员中的“一种”,而不是为每一个数据成员配置空间,在union 中所有的数据成员共用一个空间,同一时间只能储存其中一个数据成员,所有的数据成员具有相同的起始地址。例子如下:
union StateMachine
{
   char character;
   int number;
   char *str;
   double exp;
};

一个union 只配置一个足够大的空间以来容纳最大长度的数据成员,以上例而言,最大长度是double 型态,所以StateMachine 的空间大小就是double 数据类型的大小。

在C++里,union 的成员默认属性页为public。union 主要用来压缩空间。如果一些数据不可能在同一时间同时被用到,则可以使用union。
一、大小端模式对union 类型数据的影响

下面再看一个例子:
union
{
   int i;
   char a[2];
}*p, u;
p =&u;
p->a[0] = 0x39;
p->a[1] = 0x38;

p.i 的值应该为多少呢?

这里需要考虑存储模式:大端模式和小端模式。
大端模式(Big_endian):字数据的高字节存储在低地址中,而字数据的低字节则存放在高地址中。
小端模式(Little_endian):字数据的高字节存储在高地址中,而字数据的低字节则存放在低地址中。
union 型数据所占的空间等于其最大的成员所占的空间。对union 型的成员的存取都是相对于该联合体基地址的偏移量为0 处开始,也就是联合体的访问不论对哪个变量的存取都是从union 的首地址位置开始。如此一解释,上面的问题是否已经有了答案呢?
二、如何用程序确认当前系统的存储模式?

上述问题似乎还比较简单,那来个有技术含量的:请写一个C 函数,若处理器是Big_endian 的,则返回0;若是Little_endian 的,则返回1。

先分析一下,按照上面关于大小端模式的定义,假设int 类型变量i 被初始化为1。

以大端模式存储,其内存布局如下图:

以小端模式存储,其内存布局如下图:

变量i 占4 个字节,但只有一个字节的值为1,另外三个字节的值都为0。如果取出低地址上的值为0,毫无疑问,这是大端模式;如果取出低地址上的值为1,毫无疑问,这是小端模式。既然如此,我们完全可以利用union 类型数据的特点:所有成员的起始地址一致。

到现在,应该知道怎么写了吧?参考答案如下:
int checkSystem( )
{
   union check
   {
      int i;
      char ch;
   } c;
   c.i = 1;
   return (c.ch ==1);
}

现在你可以用这个函数来测试你当前系统的存储模式了。当然你也可以不用函数而直接去查看内存来确定当前系统的存储模式。如下图:

图中0x01 的值存在低地址上,说明当前系统为小端模式。

不过要说明的一点是,某些系统可能同时支持这两种存储模式,你可以用硬件跳线或在编译器的选项中设置其存储模式。

留个问题:在x86 系统下,输出的值为多少?
#include <stdio.h>
intmain()
{
   int a[5]={1,2,3,4,5};
   int *ptr1=(int *)(&a+1);
   int *ptr2=(int *)((int)a+1);
   printf("%x,%x",ptr1[-1],*ptr2);
   return 0;
}

(C语言)共用体union的用法举例

以前在学校学习C语言的时候一直搞不懂那个共用体union有什么用的。工作之后才发现它的一些妙用,现举例如下:

1. 为了方便看懂代码。

比如说想写一个3 * 3的矩阵,可以这样写:
[ 注:下面用红色部分标记的地方是后来添加上去的,谢谢yrqing718的提醒!]

struct  Matrix
{
    union
    {
        struct
        {
            float  _f11, _f12, _f13, _f21, _f22, _f23, _f31, _f32, _f33;
        };
        float  f[3][3];
    }_matrix;
};
struct  Matrix m;

这两个东西共同使用相同的空间,所以没有空间浪费,在需要整体用矩阵的时候可以用
m._matrix.f (比如说传参,或者是整体赋值等);需要用其中的几个元素的时候可以用m._matrix._f11那样可以避免用m.f[0][0](这样不大直观,而且容易出错)。

2. 用在强制类型转换上(比强制类型转换更加容易看懂)
下面举几个例子:

(1). 判断系统用的是big endian 还是 little endian(其定义大家可以到网上查相关资料,此略)

#define TRUE 1
#define FALSE 0
#define BOOL int

BOOL  isBigEndian()
{
    int  i = 1;   /* i = 0x00000001*/
    char  c = *(char  *)&i; /* 注意不能写成 char c = (char)i; */
    return  (int )c != i;
}
如果是little endian字节序的话,那个i = 1;的内存从小到大依次放的是:0x01 0x00 0x00 0x00,如是,按照i的起始地址变成按照char *方式(1字节)存取,即得c = 0x01;
反之亦然

也许看起来不是很清晰,下面来看一下这个:
BOOL  isBigEndian()
{
    union
    {
        int  i;
        char  c;
    }test;
    
    test.c = 2;
 
    return  test.i != 2;
}
这里用的是union来控制这个共享布局,有个知识点就是union里面的成员c和i都是从低地址开始对齐的。同样可以得到如此结果,而且不用转换,清晰一些。

什么,不觉得清晰??那再看下面的例子:

(2). 将little endian下的long long类型的值换成 big endian类型的值。已经知道系统提供了下面的api:long htonl(long lg);作用是把所有的字节序换成大端字节序。因此得出下面做法:

long  long  htonLL(long  long  lg)
{
    union  
    {
        struct  
        { 
            long  low;
            long  high;
        }val_1;
        long  long  val_2;
    }val_arg, val_ret;
    if ( isBigEndian() )
        return  lg;
    val_arg.val_2 = lg;
    val_ret.val_1.low = htonl( val_arg.val_1.high );
    val_ret.val_1.high = htonl( val_arg.val_1.low );    
    return  val_ret.val_2;
}
只要把内存结构的草图画出来就比较容易明白了。

(3).为了理解c++类的布局,再看下面一个例子。有如下类:

class  Test
{
public :
    float  getFVal(){ return  f;}
private :
    int  i;
    char  c;
    float  f;
};
Test t;

不能在类Test中增加代码,给对象中的f赋值7.0f.

class  Test_Cpy
{
 public :
    float  getVal(){ return  f;}
    float  setVal(float  f){ this ->f = f;}
private :
    int  i;
    char  c;
    float  f;
};
....
int  main()
{
    Test t;
    union
    {
         Test t1, 
         Test_Cpy t2;
    }test;
    test.t2.setVal(7.0f);
    t = test.t1;
    assert( t.getVal() == 7.0f );   
    return  0;
}
说明:因为在增加类的成员函数时候,那个类的对象的布局基本不变。因此可以写一个与Test类一样结构的类Test_Cpy,而多了一个成员函数setVal,再用uinon结构对齐,就可以给私有变量赋值了。(这种方法在有虚机类和虚函数机制时可能失灵,故不可移植)至于详细的讨论,网上有,这个例子在实际中没有用途,只是用来考察这个内存布局的使用而已.

union在操作系统底层的代码中用的比较多,因为它在内存共赏布局上方便且直观。所以网络编程,协议分析,内核代码上有一些用到union都比较好懂,简化了设计。

 

 

 

联合体与结构体是很容易混淆的概念。粗略一看,两者无论声明、定义还是定义对象的方式都很相似。然而这两个东西的概念和作用实际千差万别。
首先,联合体的各个成员共用内存,并应该同时只能有一个成员得到这块内存的使用权(即对内存的读写),而结构体各个成员各自拥有内存,各自使用互不干涉。所以,某种意义上来说,联合体比结构体节约内存。
举个例子:
typedef struct
{
    int i;
    int j;
}A;
typedef union
{
  int i;
  double j;
}U;
sizeof(A)的值是8,sizeof(U)的值也是8(不是12)。
为什么sizeof(U)不是12呢?因为union中各成员共用内存,i和j的内存是同一块。而且整体内存大小以最大内存的成员的划分。即U的内存大小是double的大小,为8了。sizeof(A)大小为8,因为struct中i和j各自得到了一块内存,每人4个字节,加起来就是8了。
了解了联合体共用内存的概念,也就是明白了为何每次只能对其一个成员赋值了,因为如果对另一个赋值,会覆盖了上一个成员的值。


例如:书包;可以放置书本、笔盒、记事本等物。
联合体,仅能放入一样东西的包(限制),其尺寸,是可放物品中,最大一件的体积。
结构体,是能放入所有物品的包,所以其尺寸,可同时容纳多样物品。

联合体,同时间只能有一个成员在内。或是说,可以用不同型态,去看同一组数据。
结构体,可以包含多个成员在一起,成员都能个别操作。

 

 

struct和union结构体和联合体的区别(转)
共用体 
构造数据类型,也叫联合体 
用途:使几个不同类型的变量共占一段内存(相互覆盖) 

结构体是一种构造数据类型 
用途:把不同类型的数据组合成一个整体-------自定义数据类型 

--------------------------------------------------------------- 


结构体变量所占内存长度是各成员占的内存长度的总和。 


共同体变量所占内存长度是各最长的成员占的内存长度。 


共同体每次只能存放哪个的一种!! 

共同体变量中起作用的成员是尊后一次存放的成员, 
在存入新的成员后原有的成员失去了作用! 

--------------------------------------------------------------- 


Structure 与 Union主要有以下区别: 

1. struct和union都是由多个不同的数据类型成员组成, 但在任何同一时刻, union中只存放了一个被选中的成员, 而struct的所有成员都存在。在struct中,各成员都占有自己的内存空间,它们是同时存在的。一个struct变量的总长度等于所有成员长度之 和。在Union中,所有成员不能同时占用它的内存空间,它们不能同时存在。Union变量的长度等于最长的成员的长度。 

2. 对于union的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于struct的不同成员赋值是互不影响的。 



举一个例子: 

例:

#include <stdio.h> 

void main() 
{       
union{                                                /*定义一个联合*/       
int i;       
struct{                                     /*在联合中定义一个结构*/       
char first;       
char second;       
}half;       
}number;       
number.i=0x4241; /*联合成员赋值*/       
printf("%c%c\n", number.half.first, number.half.second);       
number.half.first='a';                  /*联合中结构成员赋值*/       
number.half.second='b';       
printf("%x\n", number.i);
                         system("pause"); 
} 

输出结果为: 
AB 
6261
分析:
union的成员是共用内存的
union{ 
int i; 
struct{ 
char first; 
char second;
}half;
}number; 
number.i=0x4241;

在这里i 和 half结构是共用内存

number.i=0x4241给i赋值后,内存中以二进制存储0100 0010 0100 0001
按顺序对应到结构中
halt.first=01000010   转换成10进制就是66(字母A的asc码)
halt.second=01000001 转换成10进制是65   (字母B的asc码)
所以输出后就是 AB

下面同理了
==========================================================================
第一题: 
#include <stdio.h> 
union 
{ 
int i; 
char x[2]; 
}a; 
void main() 
{ 
a.x[0] = 10; 
a.x[1] = 1; 
printf("%d",a.i); 
} 
答案:266 
第二题: 
main() 
{ 
union{ /*定义一个联合*/ 
int i; 
struct{ /*在联合中定义一个结构*/ 
char first; 
char second; 
}half; 
}number; 
number.i=0x4241; /*联合成员赋值*/ 
printf("%c%c\n", number.half.first, mumber.half.second); 
number.half.first='a'; /*联合中结构成员赋值*/ 
number.half.second='b'; 
printf("%x\n", number.i); 
getch(); 
} 
答案: AB 6261
C语言中的联合体(UNION)的概念是,联合体中的多种数据类型共享同一个内存空间。就拿你举的例子来说: 
union 
{ 
int i; 
char x[2]; 
}a; 
在联合体a中定义了两种数据类型,字符数组x以及整形变量i.其中整形变量是16位的,数组大小为2的字符数组为8X2=16位。如此一来,编译器便会为 联合体a在内存中开辟一个16位的空间,这个空间里存储联合体的数据,但是这个空间只有16位,它既是整形变量的数据,也是字符数组的数据。如果你的程序 从字符数组的角度解析这个空间,那么它就是两个字符,如果你的程序从整型的角度解析这个空间,那么它就是一个整数。 
以你的程序为例子,现在已经开辟了一个16位的空间,然后我们假定现在空间还没有被赋值,为: 
00000000 00000000 
那么在运行完代码 
a.x[0] = 10; 
a.x[1] = 1; 
之后,16位的空间变为: 
00000110 00000001 
然后程序运行 
printf("%d",a.i); 
就是把联合体a当成一个整数来解析,而不是字符串数组。那么这样一来,程序就把这16位变成了一个完整的整数: 
(00000001 00000110)二进制 = (266)十进制 
注意,你可以看到程序在把16位弄成整数的时候把后面八位放在了前面,前面八位放在了后面。这个反序是计算机存储结构造成的,这个和联合体没有直接关系。如果感兴趣的话可以参考汇编语言。 
就是这个道理。
第二个例子同样, 
union{ /*定义一个联合*/ 
int i; 
struct{ /*在联合中定义一个结构*/ 
char first; 
char second; 
}half; 
}number; 

定义了联合体number,这个联合体有两种数据类型,整形i(16位),以及一个结构体(struct half)(2个char,16位)。所以编译器为这个联合体开辟一个16位的空间: 
00000000 00000000 
然后赋值: 
number.i=0x4241; 
这个时候,联合体以整形的身份出现,16位的空间将被整体认为是一个整数赋值。 
注意(0x4241)(16进制) = (01000010 01000001)二进制。还记得刚才说的,计算机存储的时候是反着存的吗,先存低位,再存高位(参考汇编语言),因此16位地址被赋值位 
01000001 01000010 
然后 
printf("%c%c\n", number.half.first, mumber.half.second); 
实际上是把16位空间以结构体half的角度解析,也就是两个char. 
那么第一个:number.half.first = (01000001)二进制 = (65)十进制 = A(ASCII码) 
同理number.half.second = B(ASCII码) 
当然后头又给first和second赋值位"a""b",这样会把16位空间弄成是: 
01100001 01100010 
然后用 
printf("%x\n", number.i); 
就是把16位看成整数,记住高地位反过来 
(01100010 01100001)二进制 =   (0X6261)16进制 
所以结果就是:0x6261. 

getch(); 
最后记得按任意键结束程序。
共用体
构造数据类型,也叫联合体
用途:使几个不同类型的变量共占一段内存(相互覆盖)
结构体是一种构造数据类型
用途:把不同类型的数据组合成一个整体-------自定义数据类型
---------------------------------------------------------------
结构体变量所占内存长度是各成员占的内存长度的总和。
共同体变量所占内存长度是各最长的成员占的内存长度。
共同体每次只能存放哪个的一种!!
共同体变量中起作用的成员是尊后一次存放的成员,
在存入新的成员后原有的成员失去了作用!
---------------------------------------------------------------
Structure 与 Union主要有以下区别:
1. struct和union都是由多个不同的数据类型成员组成, 但在任何同一时刻, union中只存放了一个被选中的成员, 而struct的所有成员都存在。在struct中,各成员都占有自己的内存空间,它们是同时存在的。一个struct变量的总长度等于所有成员长度之和。在Union中,所有成员不能同时占用它的内存空间,它们不能同时存在。Union变量的长度等于最长的成员的长度。
2. 对于union的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于struct的不同成员赋值是互不影响的。
举一个例子:
例:
#include <stdio.h>
void main()
{      
                         union{                                                      
                                    int i;      
                                    struct{                                           
                                              char first;      
                                              char second;      
                                              }half;      
                                }number;      
                          number.i=0x4241;       
                        printf("%c%cn", number.half.first, number.half.second);      
                          number.half.first='a';                        
                         number.half.second='b';      
                         printf("%xn", number.i);      
                       }
输出结果为:
AB
6261
结构体由一个或者多个相同或者不同的类型的数据组成的一个数据结构,系统在分配空间是会为所有的数据分配空间,因此结构体占用的存储空间至少是他的所有数据的长度之和(由于数据对齐的问题,分配空间可能大于所有数据的长度和);联合体则是一种合并式的数据类型,它可以在不同的时刻存储联合体中定义的一个数据;他的存储空间是它的所有数据中最长的那个数据的长度;
比如:
typedef struct {
int a; //4个字节
long b; //4个字节
long long c; //8个字节
char *d ; //4个字节
char e; //4个字节,其中一个字节有效,其他为对齐需要
}A;
这个结构体实际分配的空间是:24个字节;他的数据实例同时保存abcde五个数据
typedef union{
int a; //4个字节
long b; //4个字节
long long c; //8个字节
char *d ; //4个字节
char e; //4个字节,其中一个字节有效,其他为对齐需要
}B;
这个联合体分配空间8个字节(c的长度),他的数据实例同时只能保存abcde中的一个

 

posted on 2014-11-06 11:44  xxxxxxxx1x2xxxxxxx  阅读(1565)  评论(0编辑  收藏  举报