【C语言】位域

开始正文之前,我们先聊聊自己的身份。
不知道大家怎么定义程序员这个职业的,某天,我看见一位博客上的程序媛,她是这么说的。
可以废寝忘食,也可低头沉思,可喃喃自语,也可捏手皱眉,这就是我们这群神奇的生物–程序员。
作为一名C/C++程序员,做任何事都必须有个前提:省空间,省时间,这也是我们毕生最求的目标。

开始今天的正题吧!
位域:嗯?位域是个什么东西呢?我的理解是,省空间的一种数据机构。
度娘给的回答是:
有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。

位域:把一个字节中的二进位划分为几 个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。

1. 位域的定义
位域的定义格式:

//这里就不写完整的C格式了。
struct test
{
	int a : 2;
	int b : 3;
	int c : 6;
};

2. 位域的赋值

struct BIT
{
	int a : 3;
	int b : 4;
]bit;

//赋值:
bit.a = 7; //ok,可以
bit.b = 16; //error,不可以
/*
注意:位域都是按照二进制存储,给位域里的类型赋值时,不能超过本身位数的最大值。
比如:
a是3位,那么按照二进制表示最大为: 0111 == 7;如果此时值大于7,那么就错了。
*/
printf("a=%d,b=%d\n",bit.a,bit.b);  //得到7,16是错的,编不过,改成16以下即可.

位域也是可以用指针表示的!我们坐下修改.
无名位域与空域,

struct BIT
{
	int a : 3;
	int b : 4;
	int3; //位域可以是无名位域,但是却不能使用
	int : 0;//空域也是可以的,一样不能使用
]bit,*pbit;

//赋值:
pbit = &bit; //得到bit的地址。
pbit->a = 3;
pbit->b& = 9;
//输出结果,正确
printf("a=%d,b=%d\n",pbit->a,pbit->b);

ps:如果int类型的位域,最大是32位,只要值不超过32位可表示的最大值都是可以的。

3. 结构体成员包含位域
这里要涉及到一点内存对齐的知识,看不懂可以跳过,后面再看,我尽量讲详细点。

#include <stdio.h>
#include <stdlib.h>

typedef struct Cain
{
	//最简单的一种内存对齐
	char a : 3;
	char b : 4;
	char c : 6;
}Cain;

int main()
{
	//我们先输出结果,再分析,用到sizeof运算符计算大小。
	printf("Cain结构体size=%d\n",sizeof(Cain)); //result: Cain结构体size=2;

	system("pause");
	return 0;
}

分析:
char == 1byte; ok,No problem,
1byte == 8bit; so easy,No problem;
结构体按照正常字节对齐肯定是3,对吧。
但是,成员是位域,所以,按照位域的对齐方式来,以最小空间作为代价.
实际:3bit + 4bit + 6bit = 13bit; 可以,这一步也没问题。
2byte == 16 bit; ok,完全正确. 所以,这个2字节怎么来的,明白了吧。我2字节能够装下,不需要3字节。

我们看下一个程序:

#include <stdio.h>
#include <stdlib.h>

struct Cain
{
	//这个就要好好考虑下按照什么对齐了.
	char a : 3;
	int b : 5;
	unsigned c : 6;
};

int main()
{
	//我们先输出结果,再分析,用到sizeof运算符计算大小。
	printf("Cain结构体size=%d\n",sizeof(struct Cain)); //result: Cain结构体size=8;

	system("pause");
	return 0;
}

分析:当结构体里面的数据类型不统一时,我们就按照内存对齐来做。
简单介绍一下字节对齐:
当结构体里面最大数据结构类型的字节数小于等于32位平台的字节数,即4字节。按照4字节数对齐即可。
当结构体里面最大数据结构类型的字节数大于32位平台的字节数,按照字节对齐的整数倍对齐,比如,char[5]占了5字节,但是编译器会优化为8字节,补齐为4字节的整数倍。位域其实也遵循这个规则,只是他缩小成二进制位对齐,但是实际计算还是按照字节来算。

扫描二维码关注公众号,回复: 11334111 查看本文章

实际大小:3bit + 4byte + 6bit,编译器会优化为4+4 = 8byte;因为8bit一个byte就可以装下,但是会补齐为4字节。

4. 位域对齐规则
使用位域的主要目的是压缩存储,其大致规则为:

  1. 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止
  2. 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
  3. 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VS采取不压缩方式,GCC采取压缩方式;
  4. 如果位域字段之间穿插着非位域字段,则不进行压缩;
  5. 整个结构体的总大小为最宽基本类型成员大小的整数倍。

位域就简单介绍到这里,字节对齐比较底层,绝对不单单是了解几个寄存器就可以搞定的。
如果后面对字节对齐有了新的认知,我会花时间写出来分享给大家。
转载请注明出处,谢谢。

猜你喜欢

转载自blog.csdn.net/m0_43458204/article/details/106590380