C语言---自定义类型:结构体(3)https://developer.aliyun.com/article/1544453
位段的内存分配
那么位段时如何改变内存的分配的呢?
- 位段的成员可以是 int unsigned int signed int 或者是 char 等类型
- 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的⽅式来开辟的。
- 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。
//struct S //{ // char a : 3; // char b : 4; // char c : 5; // char d : 4; //}; //int main() //{ // struct S s; // // return 0; //} /* 因为这里的数据是char类型的,1个字节,8个比特位 那么我们先开辟8个比特位,我们先用,不够的话再开辟 1.给定了空间后,在空间内部是从左向右使用还是从右向左使用呢?这个是不确定的 c语言并没与规定这个方向 那么我们假设从右到左 1 2 3 4 5 6 7 8---这里表示的是比特位的位置 b b b b a a a 放完a和b还只有一个比特位了,不够的话我们再开辟一个字节 2.当剩下的空间不足以存放喜爱一个成员的时候,空间是浪费还是使用,这个是不确定的 那么我们就假设:是浪费 //那么1号位就浪费了 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 b b b b a a a c c c c c 放完c之后,就放d,d要4个比特位,但是现在只剩下3个比特位了 那我们就再次开辟一个字节 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 b b b b a a a c c c c c d d d d d 此时s b c d都已经放好了,我们用了3个字节 假如我们不浪费的话只要2个字节就够了 那么我们来测试一下 */ //struct S //{ // char a : 3; // char b : 4; // char c : 5; // char d : 4; //}; //int main() //{ // struct S s; // printf("%zd\n", sizeof(struct S));//3 // return 0; //} /* 这个结果和我们判断的是一样的,占了3个字节大小的空间 可事实真的是这样的么? 那么我们就再举个例子进行判断 */ struct S { char a : 3; char b : 4; char c : 5; char d : 4; }; int main() { struct S s = {0};//将每个比特位都设置为0 s.a = 10; s.b = 12; s.c = 3; s.d = 4; printf("%zd\n", sizeof(struct S)); return 0; } /* //初始化的情况如下 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24-----比特位 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 b b b b a a a 0 0 0 c c c c c 0 0 0 0 d d d d */ /* s.a = 10; 往a里面放10,,10的二进制序列就是1010,可是a只有3个比特位,这里的10是4个比特位,3个比特位放不下1010 那么我们只能从地位到高位进行存放,存的是010 s.b = 12; 往b里面放12,12的二进制位是1100,b是4个比特位,那么刚好放得下1100 s.c = 3; 往c里面放3,c占5个比特位,3的二进制是11,那么我们就将00011放进去 前三个比特位还是0 s.d = 4; 往d里面放4,4的二进制是100,d占4个比特位,那么我们就将0100放进去,第一个 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24-----比特位 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 b b b b a a a 0 0 0 c c c c c 0 0 0 0 d d d d 0 1 1 0 0 0 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 1 0 0 放置结果如上: 因为4个二进制位写一个16进制,那么我们将16进制表示出来 6 2 0 3 0 4 我们进行调试,和我们分析的是一模一样的 */ /*总结: 在当前的vs环境下,开辟空间是从右向左使用,如果剩下的空间不够,那我们就浪费这些空间,再开辟一个字节 */ /* 我们再将一开始的例子拿过来进行判断下 struct A { int _a:2; int _b:5; int _c:10; int _d:30; }; 因为类型是int 那么我们开辟4个字节,23个比特位,但是a只要2个比特位,剩下30个比特位, b用5个,还有25个比特位 c用10个,还有15个比特位 d说要用30个比特位,剩下的15个不够了 那么我们再开辟4个字节,就是32个比特位 那么我们给d用30个比特位,剩下2个 算下来我们总共开辟了8个字节,浪费了17个比特位 这就是我们之前为什么算出来是8个比特位了 */
1.给定了空间后,在空间内部是从右向左使用
2.当剩下的空间不足以存放喜爱一个成员的时候,空间是浪费的
上面两种是不确定的,需要我们实时进行探究
位段的跨平台问题
- int 位段被当成有符号数还是⽆符号数是不确定的。
- 位段中最⼤位的数⽬不能确定。(16位机器最⼤16,32位机器最⼤32,写成27,在16位机器会
出问题。
- 位段中的成员在内存中从左向右分配,还是从右向左分配,标准尚未定义。
- 当⼀个结构包含两个位段,第⼆个位段成员⽐较⼤,⽆法容纳于第⼀个位段剩余的位时,是舍弃
剩余的位还是利⽤,这是不确定的。
总结:
跟结构相⽐,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。
位段的注意事项
位段的⼏个成员共有同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位
置处是没有地址的。内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的。
所以不能对位段的成员使⽤&操作符,这样就不能使⽤scanf直接给位段的成员输⼊值,只能是先输⼊
放在⼀个变量中,然后赋值给位段的成员。
struct A { int _a : 2; int _b : 5; int _c : 10; int _d : 30; }; int main() { struct A sa = { 0 }; //scanf("%d", &sa._b);//这是错误的 //正确的⽰范 int b = 0; scanf("%d", &b); sa._b = b;//直接进行赋值 return 0; }