C语言联合体(union)的使用方法及其本质-union

转载自:https://blog.csdn.net/si_zhou_qun_84342712/article/details/53187106

1.联合体union的基本特性——和struct的同与不同

union,中文名“联合体、共用体”,在某种程度上类似结构体struct的一种数据结构,共用体(union)和结构体(struct)同样可以包含很多种数据类型和变量。 不过区别也挺明显: 结构体(struct)中所有变量是“共存”的——优点是“有容乃大”,全面;缺点是struct内存空间的分配是粗放的,不管用不用,全分配。 而联合体(union)中是各变量是“互斥”的——缺点就是不够“包容”;但优点是内存使用更为精细灵活,也节省了内存空间。

2.双刃剑——多种访问内存途径共存

 1 //example
 2 #include<stdio.h>
 3 union var{
 4         long int l;
 5         int i;
 6 };
 7 main(){
 8         union var v;
 9         v.l = 5;
10         printf("v.l is %d\n",v.i);
11         v.i = 6;
12         printf("now v.l is %ld! the address is %p\n",v.l,&v.l);
13         printf("now v.i is %d! the address is %p\n",v.i,&v.i);
14 }
15 结果:
16 v.l is 5
17 now v.l is 6! the address is 0xbfad1e2c
18 now v.i is 6! the address is 0xbfad1e2c 

管union的叫共用体还真是贴切——完全就是共用一个内存首地址,并且各种变量名都可以同时使用,操作也是共同生效。如此多的access内存手段,确实好用,不过这些“手段”之间却没法互相屏蔽——就好像数组+下标和指针+偏移一样。

上例中我改了v.i的值,结果v.l也能读取,那么也许我还以为v.l是我想要的值呢,因为上边提到了union的内存首地址肯定是相同的,那么还有一种情况和上边类似:

一个int数组变量a,一个long int(32位机中,long int占4字节,与int相同)变量b,我即使没给int变量b赋值,因为数据类型相同,我使用int变量b也完全会拿出int数组a中的a[0]来,一些时候一不小心用上,还以为用的就是变量b呢~

这种逻辑上的错误是很难找出来的(只有当数据类型相去甚远的时候稍好,出个乱码什么的很容易发现错误)。

3.联合体union和大小端(big-endian、little-endian):

 1 #include<stdio.h>
 2 union var{
 3         char c[4];
 4         int i;
 5 };
 6
 7 int main(){
 8         union var data;
 9         data.c[0] = 0x04;//因为是char类型,数字不要太大,算算ascii的范围~
10         data.c[1] = 0x03;//写成16进制为了方便直接打印内存中的值对比
11         data.c[2] = 0x02;
12         data.c[3] = 0x11;
13 //数组中下标低的,地址也低,按地址从低到高,内存内容依次为:04,03,02,11。总共四字节!
14 //而把四个字节作为一个整体(不分类型,直接打印十六进制),应该从内存高地址到低地址看,0x11020304,低位04放在低地址上。
15         printf("%x\n",data.i);
16 } 

结果:
11020304
证明我的32位linux是小端(little-endian)

4.联合体union所占内存空间大小:

首先,union的首地址是固定的,那么,union到底总共有多大?根据一些小常识,做个不严谨不高深的基础版验证吧。

根据:分配栈空间的时候内存地址基本上是连续的,至少同类型能保证在一起,连续就说明,我如果弄三个结构体出来,他们三个地址应该连着,看一下三个地址的间隔就知道了。

 1 #include<stdio.h>
 2 union sizeTest{
 3         int a;
 4         double b;
 5 };
 6 main(){
 7         union sizeTest unionA;
 8         union sizeTest unionB;
 9         union sizeTest unionC;
10
11         printf("the initial address of unionA is %p\n",&unionA);
12         printf("the initial address of unionB is %p\n",&unionB);
13         printf("the initial address of unionC is %p\n",&unionC);
14 }  

打印,可以看到结果:

the initial address of unionA is 0xbf9b8df8
the initial address of unionB is 0xbf9b8e00
the initial address of unionC is 0xbf9b8e08

很容易看出,8,0,8,这间隔是8字节,按double走的。

怕不保险,再改一下,把int改成数组,其他不变:

1 union sizeTest{
2         int a[10];
3         double b;
4 }; 

打印

the initial address of unionA is 0xbfbb7738
the initial address of unionB is 0xbfbb7760
the initial address of unionC is 0xbfbb7788

88-60=28
60-38=28
算错了?我说的可是16进制0x。那么0x28就是40个字节,正好是数组a的大小。

似乎忘了一个功能——sizeof()

用sizeof直接看,就知道union的大小了

1         printf("the sizeof   of unionA is %d\n",sizeof(unionA));
2         printf("the sizeof   of unionB is %d\n",sizeof(unionB));
3         printf("the sizeof   of unionC is %d\n",sizeof(unionC));
4         printf("the sizeof   of union is %d\n",sizeof(union sizeTest));  

5.联合体union适用场合:

有了前边那个验证,基本可以确认,union的内存是照着里边占地儿最大的那个变量分的。

也就可以大胆的推测一下,这种union的使用场合,是各数据类型各变量占用空间差不多并且对各变量同时使用要求不高的场合(单从内存使用上,我觉得没错)。

像上边做的第二个测试,一个数组(或者更大的数组int a[100]),和一个或者几个小变量写在一个union里,实在没什么必要,节省的空间太有限了,还增加了一些风险(最少有前边提到的逻辑上的风险)。所以,从内存占用分析,这种情况不如直接struct。

不过话说回来,某些情况下虽然不是很节约内存空间,但是union的复用性优势依然存在啊,比如方便多命名,这种“二义性”,从某些方面也可能是优势。这种方法还有个好处,就是某些寄存器或通道大小有限制的情况下,可以分多次搬运。

6.本质&进阶:

根据union固定首地址和union按最大需求开辟一段内存空间两个特征,可以发现,所有表面的定义都是虚的,所谓联合体union,就是在内存给你划了一个足够用的空间,至于你怎么玩~它不管~!(何止是union和struct,C不就是玩地址么,所以使用C灵活,也容易犯错)

没错,union的成员变量是相当于开辟了几个接口(即union包含的变量)!但是,没开辟就不能用了?当然也能用!

写个小测试:

 1 #include<stdio.h>
 2 union u{
 3         int i;
 4         double d;//这个union有8字节大小
 5 };
 6 main(){
 7         union u uu;
 8         uu.i = 10;
 9         printf("%d\n",uu.i);
10
11         char * c;
12         c = (char *)&uu;//把union的首地址赋值、强转成char类型
13         c[0] = ‘a‘;
14         c[1] = ‘b‘;
15         c[2] = ‘c‘;
16         c[3] = ‘\0‘;
17         c[4] = ‘d‘;
18         c[5] = ‘e‘;
19 //最多能到c[7]
20         printf("%s\n",c);//利用结束符‘\0‘打印字符串"abc"
21         printf("%c %c %c %c %c %c\n",c[0],c[1],c[2],c[3],c[4],c[5]);
22 }  

原文地址:https://www.cnblogs.com/zhangjxblog/p/8743203.html

时间: 2024-11-10 14:37:50

C语言联合体(union)的使用方法及其本质-union的相关文章

20160402_[转]联合体(union)的使用方法及其本质

原文:http://blog.csdn.net/huqinwei987/article/details/23597091 1.联合体union的基本特性——和struct的同与不同 union,中文名“联合体.共用体”,在某种程度上类似结构体struct的一种数据结构,共用体(union)和结构体(struct)同样可以包含很多种数据类型和变量. 不过区别也挺明显: 结构体(struct)中所有变量是“共存”的——优点是“有容乃大”,全面:缺点是struct内存空间的分配是粗放的,不管用不用,全

联合体(union)的使用方法及其本质

1.联合体union的基本特性——和struct的同与不同 union,中文名“联合体.共用体”,在某种程度上类似结构体struct的一种数据结构,共用体(union)和结构体(struct)同样可以包含很多种数据类型和变量. 不过区别也挺明显: 结构体(struct)中所有变量是“共存”的——优点是“有容乃大”,全面:缺点是struct内存空间的分配是粗放的,不管用不用,全分配. 而联合体(union)中是各变量是“互斥”的——缺点就是不够“包容”:但优点是内存使用更为精细灵活,也节省了内存空

Swift语言中的属性,方法,下标脚本以及继承

从这篇章节起,Swift编程语言指南大部分的重要内容在于概念,代码并不是太多.理解Swift的面向对象理念,语法以及类结构,构造析构过程对于很好的应用Swift语言将会有比较大的帮助. 属性 存储属性 存储属性通常是那些可以通过直接赋值,或者直接访问成员能够获得的属性类型. 它有些要注意的地方: 若一个结构体被声明为常量,则子属性无法被修改了.在Objective-C中,我们总是无法修改结构体的子属性,但是swift却可以,不过这种情况是个例外,当你存储型属性是个结构体并且是个常量,那你就不要再

c语言的宏的使用方法(转自他人)

C语言宏定义技巧 周四, 2008年 10月 09日 14:10 高级管理员 C/C++编程 - C语言基础   1,防止一个头文件被重复包含 #ifndef COMDEF_H #define COMDEF_H //头文件内容 #endif 2,重新定义一些类型,防止由于各种平台和编译器的不同,而产生的类型字节数差异,方便移植. typedef  unsigned char      boolean;     /* Boolean value type. */typedef  unsigned

《一种策略融合的跨语言文本情感倾向判别方法》论文学习笔记(大一下)

现象:因特网资源呈现多语言化和跨语言的特点,给普通用户获取非母语网络信息造成障碍. 目标:整合多语言倾向信息,以通用的数据形式让用户了解多语言数据对某个对象的评价. 针对跨语言情感倾向分类任务,提出两种跨语言情感倾向分析策略: 半监督框架的跨言情感倾向判别方法(双语协同文本情感倾向判别框架) 关键:跨语言一致文本 材料:源语言数据集和目标语言数据集 目标:利用源语言数据集的情感倾向标签,预测目标语言数据集中未标注样本点的情感倾向标签,即学习跨语言函数. 方法:将情感倾向一致样本点作为载体,采用半

对C语言进行调试的最好方法是什么?

要了解调试程序的最好方法,首先要分析一下调试过程的三个要素: 应该用什么工具调试一个程序? 用什么办法才能找出程序中的错误? 怎样才能从一开始就避免错误? 应该用什么工具调试一个程序? 有经验的程序员会使用许多工具来帮助调试程序,包括一组调试程序和一些"lint”程序,当然,编译程序本身也是一种调试工具. 在检查程序中的逻辑错误时,调试程序是特别有用的,因此许多程序员都把调试程序作为基本的调试工具.一般来说,调试程序能帮助程序员完成以下工作: (1)观察程序的运行情况 仅这项功能就使一个典型的调

C语言处理CSV文件的方法(一)

什么是CSV文件 CSV是 Comma-separated values (逗号分隔值)的首字母缩写,它通常是以逗号且不仅限于逗号分隔各个值,我们都叫他CSV. 看下面的例子: China, Shanghai, Pudong, Zhang San, 200000, 1234567 BMW; GER; 300000; RMB; i530 从上面两个例子可以看出,可以用不同的分隔符来分隔数据:数据的类型可以不同:长度任意. 由多行这样的CSV记录组成的文件叫做CSV文件(逗号分隔值文件).当然,他们

网络工程 POST与GET请求方法的本质区别

POST与GET请求方法的本质区别: 第一:GET用于信息获取,它是安全的(这里安全的含义是指非修改信息),而POST是用于修改服务器上资源的请求 第二:GET请求的数据会附在URL之后,而POST把提交的数据则放置在HTTP报文实体的主体里,所以,POST的安全性要比GET的安全性高 第三:对于POST的请求数据量比GET高是个误区,真正影响请求数据量的是服务器,服务器的限制才是真正的原因.

C#扫盲之:带你掌握C#的扩展方法、以及探讨扩展方法的本质、注意事项

1.为什么需要扩展方法 .NET3.5给我们提供了扩展方法的概念,它的功能是在不修改要添加类型的原有结构时,允许你为类或结构添加新方法. 思考:那么究竟为什么需要扩展方法呢,为什么不直接修改原有类型呢? 首先,假设我们的项目中有一个类,后来过了一段时间,我们明确的知道需要为该类添加一个新功能,考虑这个需求有两个解决办法: (1)直接修改当前类的定义 这样做的缺点是,破坏向后的兼容性,可能以前使用的旧代码无法通过编译.比如说旧代码使用了一个Methed(int,int)的方法,但是为了满足新功能我