位段、联合体(共用体)详解、枚举 -------- 用共用体判断大小端

一、位段

1.什么是位段?
有些数据在存储时并不需要占用一个完整的字节,只需要占用一个或几个二进制位即可。例如开关只有通电和断电两种状态,用 0 和 1 表示足以,也就是用一个二进位。正是基于这种考虑,C语言又提供了一种叫做位域的数据结构。
位段的声明和结构是类似的,有两个不同:

  • .位段的成员必须是 int、unsigned int 或signed int 。
  • 位段的成员名后边有一个冒号和一个数字。
    比如:
//这里的A就是一个位段类型
// :后面的数字用来限定成员变量占用的位数
//若一个成员没有限制(即后面没有冒号和数字),根据数据类型即可推算出它占用 几个字节(Byte)的内存,如下面的m,就占用四个字节。
struct A
 {
     int _a:2;
     int _b:5;
      int _c:10;
      int _d:30;
      int m ;
  };

注意点:
因为位段使数据占用的存储空间受限,因此该数据的取值范围也变的非常有限,很容易出现溢出。当数据溢出时,超出部分数据将被截去。
例如:

#include<stdio.h>
struct A
{
	int a:2;
	int b:5;
	int c:8;
 }; 
int main()
{
	struct A A={1,40,32};
	
	//%#x 是指以十六进制 0x....的形式输出
	printf("%#x,%#x,%#x",A.a,A.b,A.c);
	return 0;
}

结果是:
在这里插入图片描述
我们进行初始化时:
a = 1,b = 40, c = 32;
以十六进制输出结果为:
a = 0x1 = 1;
b = 0x8 = 8;
c = 0x20 = 32;
显然:b的结果不对,原因是什么呢?
1转换为而二进制为01,需要1位二进制就可以存下,a占用两位,可以存下因此a的大小就是1;
32转换而二进制为 100000 需要 6位才能存下,c的大小为8位因此也可以存下。
40 转换为二进制为 101000 需要6 位才能存下,而b只有5位的空间,因此存不下,那么b就只保存低位的5位 即 01000 转换为十六进制就是 0x8;
这就是存在溢出时,数据的存储方式。

2.位段的存储
C语言标准并没有规定位段的具体存储方式,不同的编译器有不同的实现,但它们都尽量压缩存储空间。
位域的具体存储规则如下:

  • 当相邻成员的类型相同时,如果它们的位宽之和小于类型的 sizeof 大小,那么后面的成员紧邻前一个成员存储,直到不能容纳为止;如果它们的位宽之和大于类型的 sizeof 大小,那么后面的成员将从新的存储单元开始,其偏移量为类型大小的整数倍。
#include <stdio.h>
int main(){
    struct bs{
        unsigned m: 6;
        unsigned n: 12;
        unsigned p: 4;
    };
    printf("%d\n", sizeof(struct bs));
    return 0;
}

m、n、p 的类型都是 unsigned int,sizeof 的结果为 4 个字节(Byte),也即 32 个位(Bit)。m、n、p 的位宽之和为 6+12+4 = 22,小于 32,所以它们会挨着存储,中间没有缝隙。
sizeof(struct bs) 的大小之所以为 4,而不是 3,是因为要将内存对齐到 4 个字节,以便提高存取效率

  • 当相邻成员的类型不同时,不同的编译器有不同的实现方案,GCC 会压缩存储,而 VC/VS 不会。
#include <stdio.h>
int main(){
    struct bs{
        unsigned m: 12;
        unsigned char ch: 4;
        unsigned p: 4;
    };
    printf("%d\n", sizeof(struct bs));
    return 0;
}

在 GCC 下的运行结果为 4,三个成员挨着存储;在 VC/VS 下的运行结果为 12,三个成员按照各自的类型存储(与不指定位宽时的存储方式相同)。

  • 如果成员之间穿插着非位域成员,那么不会进行压缩,与结构体的存储方式相同。
struct bs{
    unsigned m: 12;
    unsigned ch;
    unsigned p: 4;
};

在各个编译器下 sizeof 的结果都是 12。

通过上面的分析,我们发现位域成员往往不占用完整的字节,有时候也不处于字节的开头位置,因此使用&获取位域成员的地址是没有意义的,C语言也禁止这样做。地址是字节(Byte)的编号,而不是位(Bit)的编号。
3.无名位段
位段的成员可以没有名称,只给出其类型和位宽
例:

struct bs{
    int m: 12;
    int  : 20;  //该位域成员不能使用
    int n: 4;
};

无名位段一般用来作填充或者调整成员位置。因为没有名称,无名位域不能使用。
上面的例子中,如果没有位宽为 20 的无名成员,m、n 将会挨着存储,sizeof(struct bs) 的结果为 4;有了这 20 位作为填充,m、n 将分开存储,sizeof(struct bs) 的结果为 8。
位段的跨平台问题:

  1. int 位段被当成有符号数还是无符号数是不确定的。
  2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问 题。
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
  4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的 位还是利用,这是不确定的。
    结论:
    跟结构相比,位段可以达到同样的效果,而且可以很好的节省空间,但是有跨平台的问题存在。

联合体(共用体)

1.联合体的定义
联合也是一种特殊的自定义类型 这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以 联合也叫共用体)。

//联合类型的声明
union Un
{
	char c;
	int i;
};
//联合变量的定义
union Un un;
//计算联合变量的大小
printf("%d\n",sizeof(un));

2.联合的特点
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得 有能力保存最大的那个成员)。
在这里插入图片描述
由此可见,联合体的两个变量功用同一块空间。
在这里插入图片描述
因为两个变量共用同一块空间,因此每一个变量值的变化可能都会影响其他变量的值。
面试题:
判断当前计算机的大小端存储
代码如下:

#include<stdio.h>
union Un{
	int i;
	char c;
	
};
int main()
{
	union Un un;
	un.i=1 ;
	//输出1,表示小端,输出0表示大端。 
	printf("%d",un.c);
}

3.联合大小的计算

  • 联合的大小至少是最大成员的大小。
  • 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
    看下面的代码:
#include<stdio.h>
union Un1{
	char c[5];
	int i;	
	
};

union Un2{
	short c[7];
	int i;
};
int main()
{
	printf("%d\n",sizeof(union Un1));
	printf("%d\n",sizeof(union Un2));
	
}

结果:
在这里插入图片描述
Un1联合体的最大成员是 c[5],大小为5字节,而最大对齐数为sizeof(int) =4,因此需要对齐到4的整数倍处,所以Un1的大小为8;
Un2联合体的最大成员是 short c[7],大小为14,最大对齐数为 sizeof(int)=4,因此对齐后联合体的大小为16;

三、枚举

1.枚举类型的定义

enum Day//星期
 {
     	Mon,
        Tues,
        Wed,
        Thur,    
         Fri,    
        Sat,    
        Sun
   }; 

以上定义的 enum Day是枚举类型。 {}中的内容是枚举类型的可能取值,也叫 枚举常量 。这些可能取值都是有值的,默认从0开始,一次递增1。
在这里插入图片描述
当然在定义枚举常量的时候也可以赋初值。
例:
在这里插入图片描述
2.枚举的优点
为什么使用枚举?
我们可以使用 #define 定义常量,为什么非要使用枚举?
枚举的优点:

  • 增加代码的可读性和可维护性
  • 和#define定义的标识符比较枚举有类型检查,更加严谨。
  • 防止了命名污染(封装)
  • 便于调试
  • 使用方便,一次可以定义多个常量
    3.枚举的使用
enum Color//星期
 {
     	red=1,
     	black=4,
     	green=3
   }; 
	enum Color clr =green;	//只能拿枚举常量给枚举变量赋值,才不会出现类型差异
	clr = 5; //不可以 

这几种结构各有其优缺点,我们要合理的使用。

发布了70 篇原创文章 · 获赞 12 · 访问量 9797

猜你喜欢

转载自blog.csdn.net/lovehang99/article/details/100571836