一、C语言的共用体union
共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。 什么意思呢,就是在同一块内存存储可以定义多个数据类型,但是在使用的时候,只有一个变量有效。 这里就有一个问题,变量有大有小呀,对的,所以这个时候共用体的空间为内部变量最大占用空间的值。 如此这般,共用体就可以通过共享存储空间,来避免当前没有被使用的变量所造成的存储空间的浪费。 共用体的成员可以使用任何数据类型,但是一个共用体所占用的存储空间的字节总数,必须保证至少足以能够容纳其占用空间字节数最大的成员。并且共用体每次只允许访问一个成员,也就是一种数据类型,确保按照正确的数据类型来访问共用体中的数据,就是你的责任了。
union [tag]
{
member definition;
member definition;
...
member definition;
} [variables];
union test
{
int i;
float f;
double d;
char str[20 ];
} data;
通过这个例子可以看到,这个结构体的大小是多少呢?可以通过程序来确认一下。
任务来了,我想让你给学生建立一个数据库,该怎么来做。
按照目前学过的知识我们的代码如下,比如先来一个李雷同学的吧:
#include <stdio.h>
#include <string.h>
union test
{
int i;
float f;
double d;
char str[20 ];
};
int main ( )
{
union test data;
printf ( "data size : %d\n" , sizeof (data));
return 0 ;
}
共用体的访问与结构体类似,也是有2种类型,我们只看看成员访问运算符.。
所以按照通用的赋值方式,来看一下是否按照我们预定的方式运行。
#include <stdio.h>
#include <string.h>
union Data {
int i;
float f;
char str[20 ];
};
int main ()
{
union Data data;
data.i = 123 ;
data.f = 456.0 ;
strcpy (data.str, "Hello World" );
printf ("data.i : %d\n" , data.i);
printf ("data.f : %f\n" , data.f);
printf ("data.str : %s\n" , data.str);
return 0 ;
}
从结果上来看,what are you弄啥嘞,感觉什么跟什么呀,只有最后的字符串是正确的,这也就间接证明了共用体使用相同的存储空间,其他类型的赋值会破坏原先的赋值,正常情况下只有最后一次的赋值才会保证正确结果。
所以,我们需要在每次赋值后直接查看结果,是可以保证结果正确的:
#include <stdio.h>
#include <string.h>
union Data {
int i;
float f;
char str[20 ];
};
int main ()
{
union Data data;
data.i = 123 ;
printf ("data.i : %d\n" , data.i);
data.f = 456.0 ;
printf ("data.f : %f\n" , data.f);
strcpy (data.str, "Hello World" );
printf ("data.str : %s\n" , data.str);
return 0 ;
}
编译运行
ALL : union1 union2 union3
union1 : union1.c
gcc -o union1 union1.c
union2 : union2.c
gcc -o union2 union2.c
union3 : union3.c
gcc -o union3 union3.c
.PHONY : clean
clean :
rm -f union1 union2 union3
$ ./union2
data .i : 1819043144
data .f : 1143139122437582505939828736.000000
data .str : Hello World
$ ./union3
data .i : 123
data .f : 456.000000
data .str : Hello World
三、除了共用体,还有什么可以节省存储? C语言的结构体位域
前面可以看到,使用unoin共用体可以节省数据的存储空间。
同样,在结构体或者共用体中,使用位域也可以达到这个效果。
先看看什么时候可以使用位域,这个特点大多数人都不会用到,用到的大部分人都基本跟底层打交道,比如驱动开发、单片机开发等。
先看一个最简单的例子,比如我们的红绿灯系统,先定义一个结构体:
typedef struct
{
unsigned int red;
unsigned int green;
unsigned int yellow;
} TrafficLight;
此时如果看一下TrafficLight结构体的大小,应该是12个字节
是我们知道对于这几种灯而言,只有2种状态,开和关,也就是1和0,也就是1个bit其实就能表达,所以针对这种情况,有了位域的概念,先看一下位域的声明:
typedef struct
{
type name : width;
}
width:为位域中位的数量,其值需要小于等于type指定的类型大小
typedef struct
{
unsigned int red : 1 ;
unsigned int green : 1 ;
unsigned int yellow : 1 ;
} TrafficLight1;
三色红绿灯加起来一共需要3个bit,所以一个无符号整型就可以容纳这些值了,此时看一下这个结构体的长度,应该为4。
当结构体或共用体中有无符号整型或有符号整型成员时,C语言允许用户指定这些成员所占用的存储位数,即位域。通过将数据存储在它们所需的最小数目的存储位内,位域能够有效地提供存储空间的利用率,但是,要注意,位域成员必须被声明为有符号整型或无符号整型。
#include <stdio.h>
int main ()
{
typedef struct
{
unsigned int red;
unsigned int green;
unsigned int yellow;
} TrafficLight;
TrafficLight trafficlight;
printf ("The size of TrafficLight %d\n" , sizeof (trafficlight));
typedef struct
{
unsigned int red : 1 ;
unsigned int green : 1 ;
unsigned int yellow : 1 ;
} TrafficLight1;
TrafficLight1 trafficlight1;
printf ("The size of TrafficLight1 %d\n" , sizeof (trafficlight1));
return 0 ;
}
编译运行
struct6 : struct6.c
gcc -o struct6 struct6.c
运行输出如下:
$ ./struct6
The size of TrafficLight 12
The size of TrafficLight1 4
扩展 既然位域指定了长度位,所以就涉及到万一赋值超过了会发生什么情况,可以通过给红绿灯赋一个大值看看。warning: implicit truncation from ‘int’ to bit-field changes value from 2 to 0 [-Wbitfield-constant-conversion] 编译有警告,不过还是生成了可执行文件,运行下看看结果吧。