位段 + 枚举 + 联合体讲解


前言 :

文章涉及到之前讲过的一些概念,不了解的读者可以去看一下

结构体及结构体内存对齐讲解

数据在内存中的存储(整数)

一.位段

(1).位段介绍

位段的声明和结构类似,但它的成员是一个或多个位的字段。这些不同长度的字段实际上存储在一个或多个整形(字符)变量中。

位段的声明和任何普通的结构成员声明相同,但有两个例外

(1). 位段的成员必须是 int、unsigned int 或signed int 或 char 或 unsigned char类型

(2). 位段的成员名后边有一个冒号和一个整数,这个整数指定该位段所占用位的数目.

// 一个例子
struct A 
{
    
       
 	unsigned int a:2;
    unsigned int b:5;  
    unsigned int c:10;  
    unsigned int d:30; 
};

(2).位段内存分配及空间开辟

#include<stdio.h>
int main()
{
    
    
    struct S
    {
    
    
        char a : 3;
        char b : 4;
        char c : 5;
        char d : 4;
    };
    struct S s = {
    
     0 };
    s.a = 10;
    s.b = 12;
    s.c = 3;
    s.d = 4;
    printf("%d\n", s);
    printf("%d\n", sizeof(s));
}

位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的

在vs2019的环境下 :

(1).内存分配时是从右向左进行分配的。

(2).当下一个位段无法容纳于上一个位段的剩余的位时,会再开辟一个空间

(3).位段的可移植性问题

(1). int 位段被当成有符号数还是无符号数是不确定的。

(2). 位段中最大位的数目不能确定。(16位机器整形变量16位,32位机器32位,写成27,在16位机器会出问题)

(3). 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。

(4).当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位 还是利用,这是不确定的。

总结: 跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。

(4).位段相关练习题

(1).

#define MAX_SIZE A+B
struct _Record_Struct
{
    
    
  unsigned char Env_Alarm_ID : 4;
  unsigned char Para1 : 2;
  unsigned char state;
  unsigned char avail : 1;
}*Env_Alarm_Record;
struct _Record_Struct *pointer = (struct _Record_Struct*)malloc
(sizeof(struct _Record_Struct) * MAX_SIZE);

 // 当A=2, B=3时,pointer分配( )个字节的空间。

首先开辟一个字节的空间,给 Env_Alarm_ID分配4位,Para1分配2位,剩下2位不足以容纳 state,再开辟一个字节空间,再开辟一个空间,分配给 avail 1位

因此 sizeof(struct _Record_Struct)的值为 3 ,结果为 3 * 2 + 3 为 9

(2).

#include<stdio.h>
int main()
{
    
    
  unsigned char puc[4];
  struct tagPIM
  {
    
    
    unsigned char ucPim1;
    unsigned char ucData0 : 1;
    unsigned char ucData1 : 2;
    unsigned char ucData2 : 3;
  }*pstPimData;
  pstPimData = (struct tagPIM*)puc;
  memset(puc,0,4);
  pstPimData->ucPim1 = 2; 
  pstPimData->ucData0 = 3;
  pstPimData->ucData1 = 4;
  pstPimData->ucData2 = 5;
  printf("%02x %02x %02x %02x\n",puc[0], puc[1], puc[2], puc[3]);
  return 0;
}


打印结果为 02 29 00 00

二.枚举

枚举顾名思义就是一一列举,把可能的取值一一列举。

 enum Day
    {
    
       
        Mon, 
        Tues,
        Wed,
        Thur,
        Fri,
        Sat,
        Sun 
    };
    enum Sex
    {
    
    
        MALE,
        FEMALE
    };
    enum Color
    {
    
    
        RED=1,
        GREEN=2,
        BLUE=4 
    }; 

以上定义的 enum Day , enum Sex , enum Color 都是枚举类型。 {}中的内容是枚举类型的可能取值,也叫枚举常量。

这些可能取值都是有值的,默认从0开始,一次递增1,当然在定义的时候也可以赋初值。

三.联合体

(1).联合体介绍

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

//联合类型的声明
 union Un 
 {
    
     
  	char c; 
    int i; 
 }; 
 //联合变量的定义 
union Un un;
union Un
{
    
     
	int i; 
	char c; 
}; 
int main()
{
    
    
	 union Un un;
	// 下面输出的结果是一样的吗?
	printf("%p\n", &(un.i));
	printf("%p\n", &(un.c));

	//下面输出的结果是什么?
	un.i = 0x11223344;
	un.c = 0x55;
	printf("%x\n", un.i);
}

因为联合体的成员公用一块空间,所以地址相同,改变 un.c 也在改变 un.i ,根据小端存储模式可知,打印结果为 11223355

在前面讲解数据在内存中的存储时,已经讲解过一种判断大小端的方法,今天我们还可以使用联合体的方式去判断

#include<stdio.h>
union Un
{
    
     
	int i; 
	char c; 
}; 
int main()
{
    
    
	union Un un;
	un.i = 1;
	if (un.c == 1)
	{
    
    
		printf("小端\n");
	}
	else
	{
    
    
		printf("大端\n");
	}
}

(2).联合体的计算

(1). 联合体的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有 能力保存最大的那个成员)

(2).当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

union Un1 
{
    
      
	char c[5]; // 对齐数为 1   5个字节
 	int i;     // 对齐数为 4   4个字节
};
union Un2 
{
    
      
	short c[7]; // 对齐数为 2   14个字节
 	int i;      // 对齐数为 4   4个字节
}; 
 //下面输出的结果是什么?
 printf("%d\n", sizeof(union Un1)); 
 printf("%d\n", sizeof(union Un2)); 

Un1最大成员的大小为 5 个字节,不是最大对齐数 4 的整数倍,因此应为 8 个字节

Un2最大成员的大小为 14 个字节,不是最大对齐数 4 的整数倍,因此应为 16 个字节

// 输出结果是什么?
#include<stdio.h>
int main()
{
    
    
  union
  {
    
    
    short k;
    char i[2]; 
  }*s, a;
  s = &a;
  s->i[0] = 0x39;
  s->i[1] = 0x38;
  printf(%x\n”,a.k);
  return 0;
}

改变 char 数组的值就是在改变 k 的值,因此 k 的值以 %x 的形式打印出来为 3839

猜你喜欢

转载自blog.csdn.net/DR5200/article/details/114487711