当前位置:   article > 正文

C语言 位段_将结构体数据按照位段存入字节数组中

将结构体数据按照位段存入字节数组中

位段介绍

位段,C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”( bit field) 。利用位段能够用较少的位数存储数据。(来自:百度百科-位段

位段的声明

主要介绍位段与结构体声明的不同
1.位段的成员必须是int、char、unsigned int、unsigned char
2.位段的成员名后有一个冒号和一个数字

位段声明示例

struct S
{
    unsigned char a:2;
    unsigned char b:5;
};
  • 1
  • 2
  • 3
  • 4
  • 5

位段成员名后的数字表示每个成员变量占据的空间,单位是位,比如该例中的 a 是两位,b 是5位,此时的两个变量总共占据了不到一个字节的空间。
我们可以通过计算S的大小来确定使用位段是否能够节省内存。

int main()
{
    printf("struct S: %zd\n", sizeof(struct S));
    printf("unsigned char: %zd\n", sizeof(unsigned char));
    return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在这里插入图片描述
sizeof 操作符计算的是数据类型占据内存的大小,单位是字节,可以看出,位段的确可以节省内存。

位段在内存中的存储

#include<stdio.h>
struct S
{
	char a : 4;
	char b : 5;
	char c : 3;
	char d : 4;
};
int main()
{
	struct S temp = { 0 };
	printf("temp : %d\n", sizeof(temp));
	temp.a = 10;
	temp.b = 15;
	temp.c = 7;
	temp.d = 3;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

在这里插入图片描述
temp的大小为3字节,但是a,b,c,d的位数加起来是16位,也就是2个字节,这说明位段并不是直接连续存储在内存中的。
可以通过调试对temp在内存中的存储进行进一步的观察。
首先(Fn+)F10,进行调试,在创建temp变量之后就可以打开调试->窗口->内存->内存1,然后在地址那里输入&temp,就可以观察temp变量的变化了。
在这里插入图片描述
继续(Fn+)F10,可以不断观察变化。在箭头指向temp.c的赋值语句时,temp.a的值是10,在内存中存储的是二进制,在VS这里是以16进制的方式显示的,也就是0a,temp.b的值是15,16进制就是0f,那个黄色的箭头代表将要执行的语句,也就是说此时的temp.c和temp.d还未赋值。
在这里插入图片描述
再(Fn+)F10,可以看到 0f 变成了 ef ,也就是说temp.b和temp.c是共用一个字节的内存。
temp.b : 0f(16进制) 00001111(2进制)
这个字节的大小变为ef,也就是11101111,而temp.c的大小刚好是7也就是111(2进制),temp.b是5位,temp.c是3位。temp.c也就是temp.b所在的这一个字节的前面三位。
在这里插入图片描述
继续(Fn+)F10,03又是另外一个字节的内容了。
从这个例子中我们可以了解到位段存储的一些规则。

一个位段必须存储在一个存储单元内,不能跨两个单元,如果第一个单元不能容纳下一个位段,则该空间不用,而从下一个单元起存放该位段,对于这个存储单元,char类型的是1个字节,int类型是4个字节

为什么temp.a是0a不是a0呢?为什么是fe不是ef?

其实这个问题和大小端相似,可以对照着理解。

位段存储-大小端

#include<stdio.h>
struct S
{
	char a : 4;
	char b : 5;
	char c : 3;
	char d : 4;
	char e : 5;
};
int main()
{
	struct S temp = { 0 };
	printf("temp : %zd\n", sizeof(temp));
	temp.a = 10;//0a
	temp.b = 15;//1110 1111
	temp.c = 7;//
	temp.d = 3;//03
	temp.e = 0x11;
	printf("%x\n", temp);//VS出现警告
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

该代码在S的定义中增加了语句 char e: 5; 让temp的大小达到4字节,以%x的形式打印temp.
在这里插入图片描述
输出结果中11是我们新加的代码temp.e = 0x11; 03ef0a是我们上面分析的代码,但是和我们在内存中看到的字节的顺序是相反的。
这里需要补充大小端的内容。
大端存储:数据的低位保存在内存的高地址中,数据的高位保存在内存的低地址中
小端存储:数据的高位保存在内存的高地址中,数据的高位保存在内存的低地址中
以数字 0x12 为例,其中的 2 就是低位,1就是高位,计算机在存储的时候可以选择将 1 存储在低地址,将 2 存储在高地址,这种就是大端存储;如果将 1 存储在高地址,将 2 存储在低地址,就是小端存储。
我们再次进行调试,并将内存窗口的列数改为1
在这里插入图片描述
通过调试,可以看到0a的地址最小,在我们的输出结果1103ef0a中,0a就是低位,表明了该电脑的存储方式是小端存储。

位段在一个字节中的存储

对于temp中的 b 和 c 来说,temp.b占了5位,temp.c占了3位,在使用temp.b的时候,我们只会使用temp.b的5个字节的内容,而在使用temp.c的时候,只会拿temp.c所在的3个字节的内容,那个变量在前或者在后对我们的使用没有影响。通过这个例子,可以得出结构体中在小端存储的电脑中,如果有多个变量共用一个字节,结构体中先定义的位段变量会存放在字节的低位,就像是temp.a在内存中是0a而不是a0一样。
对于大端存储的电脑,与小端存储的相反。

那么如果我们交换b和c的顺序,会出现fe吗?

#include<stdio.h>
struct S
{
	char a : 4;
	char c : 3;
	char b : 5;
	char d : 4;
	char e : 5;
};
int main()
{
	struct S temp = { 0 };
	temp.a = 10;//0a
	temp.b = 15;//1110 1111
	temp.c = 7;//
	temp.d = 3;//03
	temp.e = 0x11;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在这里插入图片描述
在结构体S中 a 和 c 是相邻的两个变量,a占4位,c占3位,temp.a和temp.c共用同一字节,因为结构体中先定义的a在字节的低位(小端存储),后4位:temp.a : 10 : 1010 (二进制): a(十六进制) ; 前4位中的后3位,temp.c : 7 : 111(二进制) : 7(十六进制),所以是7a。因为在刚开始的时候temp就被赋值成了0,所以前4位中的第一位是0。

如果此时再将a和c的位置交换一下结果会是什么呢?会出现a7吗?

#include<stdio.h>
struct S
{
	char c : 3;
	char a : 4;
	char b : 5;
	char d : 4;
	char e : 5;
};
int main()
{
	struct S temp = { 0 };
	temp.a = 10;//0a
	temp.b = 15;//1110 1111
	temp.c = 7;//
	temp.d = 3;//03
	temp.e = 0x11;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在这里插入图片描述
此时c是结构体中先定义的,所以第一个字节的后3位是c,也就是7:111(二进制),c变量前面的4位是a,就是10:1010(二进制),因为开始时,temp被初始化为0,该字节第一位没被赋值,保持不变,仍然是0,所以该字节的内容是01010111,就是57(16进制)。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/515900
推荐阅读
相关标签
  

闽ICP备14008679号