【C语言】位段、枚举、联合体知识点

位段

1. 简单介绍

struct S
{
    char a : 3;
    char b : 4;
    char c : 5;
    char d : 4;
};

int main()
{
    struct S s ={0};
    s.a=10;//1010
    s.b=12;//1100
    s.c=3;//0011
    s.d=4;//0100
    
    return 0;
}
//char - 1字节 - 8比特位
//照下图的位段放置,结果是:
//0110 0010 0000 0011 0000 0100
// 6    2    0    3    0    4

image-20220919191514560

  1. 位段最大位数目不能确定(机器位数不确定)(存在跨平台问题)
  2. 位段中成员在内存中无分配方向标准(右->左 / 左->右)
  3. 舍弃剩余的位与否不确定
  4. 最高位是不是符号位不确定

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

2. 例题

#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分配多少字节空间?

结构体向最长的char对齐,前两个位段元素一共4+2位,不足8位,合起来占1字节,最后一个单独1字节,一共3字节。

另外,这道题的考点不止位段,还考了 #define 的查找替换,这题替换过后是 3 * A + B,即 3 * 2 + 3 = 9

所以这题中pointer分配了9个字节的空间

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;
}
//这段代码的结果是?

puc是一个char数组,每次跳转一个字节,结构体不是,它只有第一个元素单独享用一字节,其他三个元素一起共用一字节,所以puc被结构体填充后,本身只有两个字节会被写入,后两个字节肯定是0。第一个字节是2就是2了,第二个字节比较麻烦,首先ucData0给了3其实是越界了,1位的数字只能是0或1,所以11截断后只有1,同理ucData1给的4也是越界的,100截断后是00,只有5的101是正常的。填充序列是类似小端的低地址在低位,所以排列顺序是00 101 00 1。也就是0010 1001,即0x29

这段代码的结果是 02 29 00 00

枚举

1. 简单介绍

enum Colour
{
    red,
    blue,
    green
};

int main()
{
    printf("%d\n",red);//-> 0
    printf("%d\n",blue);//-> 1
    printf("%d\n",green);//-> 2
    red=3;//报错
    enum Colour colour = 5;//错误哦,类型不一致
    return 0;
}

从这段代码我们可以发现

  1. 枚举使得代码可读性更强
  2. 枚举定义常量,且一次可定义多个

从一次可定义多个常量这点就可以看出枚举相较于#define的优越性,实际上相较于#define,枚举还有隐性的优点

  1. 不同于#define在预编译后就销毁数据,枚举不会,这使得其便于调试
  2. 枚举提供类型检查,更为可靠

2. 例题

enum ENUM_A
{
		X1,
		Y1,
		Z1 = 255,
		A1,
		B1,
};
enum ENUM_A enumA = Y1;
enum ENUM_A enumB = B1;
printf("%d %d\n", enumA, enumB);
//这段代码运行的结果是?

枚举默认从0开始,所以X1是0,故Y1是1,给了数字后会根据数字向后推,那么Z1是255,A1是256,所以B1是257

如果不知道给定数字后向后推,那就很迷茫了。当然,知道这个知识点之后这题是很无脑的。

联合(联合体)

1. 简单介绍

union UN
{
    char c;// 1
    int i;// 4
    double d;// 8
};

int main()
{
	union UN un;
    printf("%d\n",sizeof(union UN));
    printf("%d\n",sizeof(UN));
    printf("%p\n",&(un));
    printf("%p\n",&(un.c));
    printf("%p\n",&(un.i));
    printf("%p\n",&(un.d));
    return 0;
}

运行后我们不难发现,以下四行代码打印出的地址一致,我们可以得到联合体的一个特征

image-20220922103023726

联合体内的元素共用一个地址,但不能同时使用

IMG_0601

关于数据的存储,一直都存在一个问题:顺序问题。只要数据的内存空间超过一个字节,就会涉及顺序的问题。

有一个经典的问题:大小端存储的判断,在这里可以用联合体来巧妙得实现判断:

int check_sys()
{
	union UN
    {
        char c;
        int i;
    }u;
    u.i=1;
    return u.c;
}

int main()
{
    int ret=check_sys();
    if(ret==1)
        printf("小端\n");
    else
        printf("大端\n");
    return 0;
}
IMG_0600

结合图像,我们能更深刻地理解联合体 共用一个地址 的含义。

2. 例题

//X86架构下,小端字节序存储,有下列代码
#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;
}
//运行结果是?

这题中union只有2字节,2字节的十六进制只有4位 。而位顺序类似小端,低地址在低处,所以39是低地址,在低位,38在高位,所以是3839
n
{
short k;
char i[2];
}*s, a;
s = &a;
s->i[0] = 0x39;
s->i[1] = 0x38;
printf(“%x\n”,a.k);
return 0;
}
//运行结果是?


> 这题中union只有2字节,2字节的十六进制只有4位 。而位顺序类似小端,低地址在低处,所以39是低地址,在低位,38在高位,所以是`3839`
  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值