![]() |
傻傻的大脸猫 · C 位域| 菜鸟教程· 1 年前 · |
![]() |
傻傻的大脸猫 · 从零开始独立游戏开发学习笔记(五十七)--编 ...· 1 年前 · |
![]() |
傻傻的大脸猫 · 作曲技术理论推荐书目二_推荐文章_琴趣乐屋· 1 年前 · |
![]() |
傻傻的大脸猫 · 学个毛对位【1】三六度- 知乎· 1 年前 · |
![]() |
傻傻的大脸猫 · [对位与赋格教程.上册]高清PDF电子书| ...· 1 年前 · |
如果程序的结构中包含多个开关的变量,即变量值为 TRUE/FALSE ,如下:
struct unsigned int widthValidated; unsigned int heightValidated; } status;这种结构需要 8 字节的内存空间,但在实际上,在每个变量中,我们只存储 0 或 1,在这种情况下,C 语言提供了一种更好的利用内存空间的方式。如果您在结构内使用这样的变量,您可以定义变量的宽度来告诉编译器,您将只使用这些字节。例如,上面的结构可以重写成:
struct unsigned int widthValidated : 1; unsigned int heightValidated : 1; } status;现在,上面的结构中,status 变量将占用 4 个字节的内存空间,但是只有 2 位被用来存储值。如果您用了 32 个变量,每一个变量宽度为 1 位,那么 status 结构将使用 4 个字节,但只要您再多用一个变量,如果使用了 33 个变量,那么它将分配内存的下一段来存储第 33 个变量,这个时候就开始使用 8 个字节。让我们看看下面的实例来理解这个概念:
当上面的代码被编译和执行时,它会产生下列结果:
Memory size occupied by status1 : 8 Memory size occupied by status2 : 4位域的特点和使用方法如下:
int
、
unsigned int
、
signed int
等整数类型,也可以是枚举类型。
.
)来实现的,与普通的结构体成员访问方式相同。
有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有 0 和 1 两种状态,用 1 位二进位即可。为了节省存储空间,并使处理简便,C 语言又提供了一种数据结构,称为"位域"或"位段"。
所谓"位域"是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。
典型的实例:
位域定义与结构定义相仿,其形式为:
struct 位域结构名其中位域列表的形式为:
type [member_name] : width ;下面是有关位域中变量元素的描述:
元素描述 type只能为 int(整型),unsigned int(无符号整型),signed int(有符号整型) 三种类型,决定了如何解释位域的值。 member_name位域的名称。 width位域中位的数量。宽度必须小于或等于指定类型的位宽度。带有预定义宽度的变量被称为 位域 。位域可以存储多于 1 位的数,例如,需要一个变量来存储从 0 到 7 的值,您可以定义一个宽度为 3 位的位域,如下:
上面的结构定义指示 C 编译器,age 变量将只使用 3 位来存储这个值,如果您试图使用超过 3 位,则无法完成。
以上代码定义了一个名为 struct bs 的结构体,data 为 bs 的结构体变量,共占四个字节:
对于位域来说,它们的宽度不能超过其数据类型的大小,在这种情况下,int 类型的大小通常是 4 个字节(32位)。
相邻位域字段的类型相同,且其位宽之和小于类型的 sizeo f大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止。
让我们再来看一个实例:
在这里,packed_struct 包含了 6 个成员:四个 1 位的标识符 f1..f4、一个 4 位的 type 和一个 9 位的 my_int。
让我们来看下面的实例:
#include <stdio.h>
在 main 函数中,创建了一个 packed_struct 类型的结构体变量 pack,并分别给每个位域成员赋值。
然后使用 printf 语句打印出每个位域成员的值。
输出结果为:
f1: 1 f2: 0 f3: 1 f4: 0 type: 7 my_int: 255
当上面的代码被编译时,它会带有警告,当上面的代码被执行时,它会产生下列结果:
Sizeof( Age ) : 4 Age.age : 4 Age.age : 7 Age.age : 0计算字节数:
#include <stdio.h>
通过 sizeof 运算符计算出 example1 结构体的字节数,并输出结果:
Size of example1: 4 bytes
对于位域的定义尚有以下几点说明:
一个位域存储在同一个字节中,如一个字节所剩空间不够存放另一位域时,则会从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:
在这个位域定义中,a 占第一字节的 4 位,后 4 位填 0 表示不使用,b 从第二字节开始,占用 4 位,c 占用 4 位。
位域的宽度不能超过它所依附的数据类型的长度,成员变量都是有类型的,这个类型限制了成员变量的最大长度, : 后面的数字不能超过这个长度。
位域可以是无名位域,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:
上例程序中定义了位域结构 bs,三个位域为 a、b、c。说明了 bs 类型的变量 bit 和指向 bs 类型的指针变量 pbit。这表示位域也是可以使用指针的。
/*age 变量将只使用 3 位来存储这个值,如果您试图使用超过 3 位,则无法完成*/ Age.age = 4; printf("Sizeof( Age ) : %d\n", sizeof(Age)); printf("Age.age : %d\n", Age.age); // 二进制表示为 111 有三位,达到最大值 Age.age = 7; printf("Age.age : %d\n", Age.age); // 二进制表示为 1000 有四位,超出 Age.age = 8; printf("Age.age : %d\n", Age.age);如果超出范围,则直接丢掉了,存不进去。
petter
don***[email protected]
karma
119***[email protected]
原则一 :结构体中元素按照定义顺序存放到内存中,但并不是紧密排列。从结构体存储的首地址开始 ,每一个元素存入内存中时,它都会认为内存是以自己的宽度来划分空间的,因此元素存放的位置一定会在自己大小的整数倍上开始。
原则二 : 在原则一的基础上,检查计算出的存储单元是否为所有元素中最宽的元素长度的整数倍。若是,则结束;否则,将其补齐为它的整数倍。
测试实例:
#include <stdio.h> typedef struct t1{ char x; int y; double z; typedef struct t2{ char x; double z; int y; int main(int argc, char* argv[]) printf("sizeof(T1) = %lu\n", sizeof(T1)); printf("sizeof(T2) = %lu\n", sizeof(T2)); return 0;sizeof(T1) = 16 sizeof(T2) = 24sizeof(T1.x) = sizeof(T2.x) = 1; sizeof(T1.y) = sizeof(T2.y) = 4; sizeof(T1.z) = sizeof(T2.z) = 8;T1: 若从第 0 个字节开始分配内存,则 T1.x 存入第 0 字节,T1.y 占 4 个字节,由于第一的 4 字节已有数据,所以 T1.y 存入第 4-7 个字节,T1.z 占 8 个字节,由于第一个 8 字节已有数据,所以 T1.z 存入 8-15 个字节。共占有 16 个字节。
T2: 若从第 0 个字节开始分配内存,则 T1.x 存入第 0 字节,T1.z 占 8 个字节,由于第一的 8 字节已有数据,所以 T1.z 存入第 8-15 个字节,T1.y 占 4 个字节,由于前四个 4 字节已有数据,所以 T1.z 存入 16-19 个字节。共占有 20 个字节。此时所占字节不是最宽元素(double 长度为 8)的整数倍,因此将其补齐到 8 的整数倍,最终结果为 24。
xkl
923***[email protected]
// 位域内存测试 #include <stdio.h> struct ONE_BYTE unsigned char _bool : 1; unsigned char del_flag : 1; unsigned char status : 4; } one_byte; struct TWO_BYTE unsigned char ccc1 : 4; unsigned char ccc2 : 4; unsigned char ccc3 : 4; unsigned char ccc4 : 4; } two_byte; struct THREE_BYTE unsigned char ccc1 : 4; unsigned char ccc2 : 4; unsigned char ccc3 : 4; unsigned char ccc4 : 4; unsigned char ccc5 : 4; } three_byte; struct FOUR_BYTE unsigned int ccc1 : 16; unsigned int ccc2 : 16; } four_byte; struct EIGHT_BYTE unsigned char ccc1 : 1; unsigned int ccc2 : 1; } eight_byte; int main(int argc, char const *argv[]) printf("sizeof one_byte is : %lu\n", sizeof(one_byte)); printf("sizeof two_byte is : %lu\n", sizeof(two_byte)); printf("sizeof three_byte is : %lu\n", sizeof(three_byte)); printf("sizeof four_byte is : %lu\n", sizeof(four_byte)); printf("sizeof eight_byte is : %lu\n", sizeof(eight_byte)); return 0;输出结果为:
sizeof one_byte is : 1B sizeof two_byte is : 2B sizeof three_byte is : 3B sizeof four_byte is : 4B sizeof eight_byte is : 4B由输出,可以验证以下结论:
(1)结构体内存分配原则:
- 原则一:结构体中元素按照定义顺序存放到内存中,但并不是紧密排列。从结构体存储的首地址开始 ,每一个元素存入内存中时,它都会认为内存是以自己的宽度来划分空间的,因此元素存放的位置一定会在自己大小的整数倍上开始。
- 原则二: 在原则一的基础上,检查计算出的存储单元是否为所有元素中最宽的元素长度的整数倍。若是,则结束;否则,将其补齐为它的整数倍。
(2)定义位域时,各个成员的类型最好保持一致,比如都用char,或都用int,不要混合使用,这样才能达到节省内存空间的目的。
木子李
Li1***[email protected]
// 二进制表示为 1000 有四位,超出 Age.age = 8; printf("Age.age : %d\n", Age.age);
超出范围并不是直接丢弃,而是保留对应的 3 位的值。
比如 8 是 00001000,按照位域,对应 3 位的值是 000,所以打印结果是 0;
但是 9 是 00001001,按照位域,对应 3 位的值是 001,所以打印结果是 1;
同理 10 是 00001010,按照位域,对应 3 位的值是 010,所以打印结果是 2;
Age.age = 9; // 二进制表示为 1001 有四位,超出 printf( "Age.age : %d\n", Age.age ); Age.age = 10; // 二进制表示为 1010 有四位,超出 printf( "Age.age : %d\n", Age.age );
// 以下是 4,7,8,9,10 的打印结果 Sizeof( Age ) : 4 Age.age : 4 Age.age : 7 Age.age : 0 Age.age : 1 Age.age : 2
xuxing
176***[email protected]
sizeof one_byte is:1 sizeof two_byte is:2 sizeof three_byte is:3 sizeof four_byte is:4 sizeof eight_byte is:8讲一下为什么 struct ONE_BYTE ,其 3 个位域都是 char 类型,所以可以按照位的顺序存储(不用分字节), 1+1+4=6 位,占用 1 个字节。
struct TWO_BYTE ,道理同上, 4+4+4+4=16 位 =2 个字节。
struct THREE_BYTE ,道理同上, 4*5=20 位,占 3 个字节(不足的 4 位补 0)。
struct FOUR_BYTE ,其 2 个位域都是 int 类型,所以可以按照位的顺序存储(不用分字节),一个 int 类型占 4 个字节,刚好 16+16=32 位 =4 个字节。为 int 类型所占字节数的 1 倍(整数倍),所以 sizeof(four_byte)=4 。
struct EIGHT_BYTE ,其 2 个位域为不同的类型,所以它们的位值不能写到一个字节里面(不能按连续字节存储),那么,ccc1 就占第 1 个字节(地址0)的第 1 位;那么再来找 ccc2 存储的字节,因为其为 int 型,而 int 型占用 4 个字节,因此系统默认就要从 4 的整数倍的地址存它,也就是说要么存第 1 到第 4 个字节(地址0-地址3),要么存第 5 到第 8 个字节(地址4-地址7),……,以此类推,但是第 1 个字节(地址0),已经存了 ccc1 了,所以它就只能存第 5 到第 8 个字节,至于其前面的 3 个字节,补0;因此最后判断 eight_byte 占用 8 个字节,最后验证 8 个字节数刚好是 EIGHT_BYTE 结构体中占用最宽字节数的类型(int型)——4个字节的整数倍(2倍),所以就是 sizeof(eight_byte)=8 。
xuxing
176***[email protected]
Ethan
186***[email protected]
@xuxing ,关于最后的 struct EIGHT_BYTE 的所占用内存大小的分析是有误的,unsigned int 类型需要用 4 个字节的内存空间来存储, unsigned char 类型需要 1 个字节的内存空间来存储,但是这里位域使用方面,cc1 和 cc2 都只使用了 1 位,即各占用 1 个 Bit,所以系统按照 cc1 的 unsigned int 类型开辟了 4 个字节来存放这个位域变量,所以最终 sizeof(eight_byte) 的结果应该是 4 才对。
当然你可以尝试将 cc2 的位域占用改成 32,这里由于 4 个字节的空间已经不够存放两个位域变量了,所以就需要按照为 int 类型所占字节数的 2 倍来为其开辟空间,即这里便需要 8 个字节了。
#include <stdio.h> struct EIGHT_BYTE unsigned char ccc1 : 1; unsigned int ccc2 : 32; } eight_byte; int main(void) printf("sizeof eight_byte is : %lu\n", sizeof(eight_byte)); return 0;得到的结果为:
sizeof eight_byte is : 8倘若将 cc2 的位域空间改成 31,则这里存储空间大小仍是 4 个字节。
#include <stdio.h> struct EIGHT_BYTE{ unsigned char ccc1 : 1; unsigned int ccc2 : 31; } eight_byte; int main(void){ printf("sizeof eight_byte is : %lu\n", sizeof(eight_byte)); return 0; 得到的结果为:sizeof eight_byte is : 4Ethan1年前 (2022-08-04)Ethan
186***[email protected]