龙空技术网

C 语言中一个位域(位段)结构占多少个字节?

Linux码农 575

前言:

此时你们对“c语言汉字几个字节”大体比较关切,小伙伴们都想要学习一些“c语言汉字几个字节”的相关文章。那么小编在网络上收集了一些有关“c语言汉字几个字节””的相关文章,希望我们能喜欢,看官们一起来了解一下吧!

在c语言中提供一个一种叫 “位域” 或者 “位段” 的数据结构。它的存在是为了更加的节省空间。因为在有些实际需求中,并不需要占用一个完整的字节,而只是需要一个或者几个二进制位。比如存在一个开关量时,只有 0 和 1 两种状态,只需要一个二进制位存储即可。

位域的定义如下:

struct 位域结构名{ 位域列表 };

其中位域列表的形式为:类型说明符 位域名:位域长度

例如,

struct test{

int a:3;

int b:2;

int c:6;

};

位域的变量的说明 与结构体的方式一样,有三种方式:先定义后说明、同时定义说明、直接说明。

在声明时,位段成员必须是整形或枚举类型(通常是无符号类型)。

结构中也可以包含无名位域,作为相邻成员之间的填充或调整位置。无名位域无法被引用,它们的内容在运行时是不可预测的。

struct s{

unsigned int a:4; //a存放结构的前4个位中

unsigned int :2; // a后面的2个位填充

unsigned int b:2;

};

位域的定义有如下限制

1、一个位域必须存储在同一个字节中,不能跨两个字节。

比如当一个字节所剩的空间不够下一个位域存储时,则从下一个存储单元的起始位置开始存放;也可以专门让某个位域从下一个存储单元的起始位置开始。

struct s{

unsigned int a:4; //a存放结构的前4个位中

unsigned int :2; // a后面的2个位填充

unsigned int b:6; // 剩余的2位不够存储,从下一个存储单元开始存放

unsigned int :0; //空位域,把该unsigned int剩余空间自动全部填充0

unsigned int c:5; //从下一个单元存储,也即是从下一个 unsigned int 开始

}ss;

在这个位域中,a 占第一个字节的前 4 位,a 后面 2 位进行填充,第一个字节剩余的 2 位不够 b 存储,因此 b 从下一个字节开始存储,b 后面的位域填充 0,c 从下一个单元存储,该下一个单元指的是下一个 unsigned int,因为空位域 0 会把 b 后面 unsigned int 所剩的空间全部填充 0, 因此 sizeof(ss) = 8。

2、位域可以是无名域,无名域就是类似于 unsigned int : 0; 或者 unsigned int : 2; 虽然两者只是 0 和非 0 的区别,但是作用却是不同的。若无名域的位数为 0,则下一个位域将会强制从下一个单元开始(这里的一个单元指的不是下一个字节,而是跨过跨过一次数据类型的自然边界);如果无名位域为非 0,则意味着这个无名位域占着空间,不能被使用。

struct test{

unsigned int a:3; //a占3位

unsigned int :0; //对于unsigned int 类型 a 后面的剩余未全填充0

unsigned int b:2; //b 从下一个unsigned int 类型开始

}tt;

在该例子中 a 占 unsigned int类型所占字节的3位,a 后面的无名域会把 a 后面的所有位填充 0,因此对于 unsigned int 类型所占的 4 字节空间,除 a 的 3 位外,其他位均为0,b 从下一个 unsigned int 类型开始,所以 sizeof(tt) 为 8。

struct test{

unsigned int a:3; //a占3位

unsigned int :2; //a后面的2个位填充

unsigned int b:2; // b 跟着无名域后面

}tt;

a后面无名域占 2 位,b跟在无名域后面,所以 a 和 b共占用一个字节。因此sizeof(tt) 为 4。

3、如果相邻位域字段的类型相同,且其位宽之和小于类型的 sizeof 大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止

struct test {

unsigned char a:3;

unsigned char b:4;

} t;

a 和 b 所占的宽度之和小于 sizeof(unsigned char)大小,因此 a和b共同使用一个字节,所以 sizeof(t) 为 1;

4、如果相邻位域字段的类型相同,但其位宽之和大于类型的 sizeof 大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍

struct test {

unsigned int a:15;

unsigned int b:20; //从下一个unsigned int开始

} t;

由于 a 和 b 宽度之和大于 sizeof(unsigned int)大小,因此b从下一个 unsigned int 处开始,所以 sizeof(t) 为 8。

5、整个结构体的总大小为最宽基本类型成员大小的整数倍。

struct test {

unsigned char a : 3;

unsigned char b : 4;

unsigned char c : 3;

unsigned int d : 5;

} t;

整个结构体的总大小为 unsigned int 类型的整数倍。

6、如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++采取压缩方式。(跟编译器有较大的关系,使用时要慎重,尽量避免)

struct test {

unsigned char a : 3;

unsigned char b : 4;

unsigned char c : 3;

unsigned int d : 5;

} t;

对于该类型,不压缩时,a和b类型相同会占用一个字节,c会占用一个字节,d会单独存放,不会和共占用一个字节,因为整个结构体的总大小为最宽基本类型成员大小的整数倍,因此 sizefo(t)为 8。

若压缩时(gcc下),c 和 d 共占用一个字节,因为整个结构体的总大小为最宽基本类型成员大小的整数倍,因此 sizefo(t)为 4。

7、 如果位域字段之间穿插着非位域字段,则不进行压缩;(不针对所有的编译器,跟编译器有较大的关系,使用时要慎重,尽量避免)

struct test{

unsigned int m: 12;

unsigned int ch;

unsigned int p: 4;

}t;

sizeof(t) 大小为 12。

8、当使用有符号类型来定义位域,并且无意中使用到了正负(有意或者无意)特性时,可能出现不是想要的结果。

struct test

{

char a : 2;

char b : 3;

char c : 3;

};

struct test t;

// 位域赋值

t.a = 0x3; // 11

t.b = 0x5; // 101

t.c = 0x2; // 010

printf("%d,%d,%d\n", t.a, t.b, t.c); //结果为 -1, -3, 2

可见,当为域的最高位是 1 的时候,会进行符号扩展,而且这也取决于编译器的实现,因此,为避免此类问题,最好使用无符号类型定义位域。

9、取地址操作符 & 不能应用在位域字段上,因此不存在位域的指针。

10、位域字段不能是静态成员。

11、不能用来指定位数的类型。

若 struct 成员是指针变量类型不能用来指定所占的位数,在 64 位系统中指针固定占 8 字节,在 32 位系统中指针固定占 4 字节。

若 struct 成员是 double 或 float 类型,不能指定位数,否则编译出错,位域类型无效。

标签: #c语言汉字几个字节