本文衔接第63篇63.【C语言】再议结构体(上)
目录
目录
6.复习
7.修改默认对齐数
8.结构体传参
01.传递非指针参数
02.传递指针参数(传递地址)
03.对比
9.结构体实现位段
01.位段的定义
02.格式
03.例题
答案速查
分析
10.位段跨平台问题
11.位段的应用
12.其他注意事项
6.复习
20.【C语言】初识结构体(重要)
48.【C语言】结构体补充
63.【C语言】再议结构体(上)
7.修改默认对齐数
加一条预处理指令
#pragma pack(对齐数)
struct 结构体标签{成员列表;}变量列表(全局变量);//变量列表可以不写,但;必须有!!!
在#pragma pack(对齐数)后紧跟着结构体就能指定该结构体的对齐数
建议给1,2的次方数:2,4,8
#include <stdio.h>
#pragma pack(1)
struct s1
{char a;char b;int c;
};
#pragma pack()//恢复默认对齐数int main()
{printf("%zd", sizeof(struct s1));return 0;
}
打印结果为6
8.结构体传参
01.传递非指针参数
#include <stdio.h>
struct s
{int data[1000];int num;
};void sim_print(struct s ss)//结构体传参
{int i = 0; for (i = 0; i < 5; i++){printf("%d ",ss.data[i]);}printf("\nnum = %d\n", ss.num);
}int main()
{struct s s = { { 1, 2, 3, 4, 5 } , 100 }; sim_print(s);return 0;
}
02.传递指针参数(传递地址)
#include <stdio.h>
struct s
{int data[1000];int num;
};//结构体传参,*ps表明传递的是指针
void sim_print(struct s *ps)
{int i = 0; for (i = 0; i < 5; i++){printf("%d ",ps->data[i]);}printf("\nnum = %d\n", ps->num);
}int main()
{struct s s = { { 1, 2, 3, 4, 5 } , 100 }; sim_print(&s);//传的是地址return 0;
}
注:结构体成员变量->成员名是结构体特有的写法
03.对比
当结构体成员变量占用较大的内存时, 因为参数要压栈,所以传递指针参数比传递非指针参数要节省空间,传递指针参数不会另外开辟额外的空间
9.结构体实现位段
01.位段的定义
摘自百度百科:
C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”(bit field),利用位段能够用较少的位数存储数据
02.格式
类型 成员变量:数字;
如: int a:5;
03.例题
求下列代码的执行后位段在内存中的数据(在VS2022中测试)
struct S
{char a : 3;char b : 4;char c : 5;char d : 4;
};int main()
{struct S s = { 0 };//注意区分大小写s.a = 10;s.b = 12;s.c = 3;s.d = 4;return 0;
}
答案速查
位段在内存中的数据:62 03 04
分析
因为 以位为单位来指定其成员所占内存长度
前置知识:位段的内存分配
2条规则,1条提醒
1.位段的成员可以是int,unsigned int,signed int,char等类型
2.位段的空间上是按照需要以4个字节(int)或者1个字节(char)的方式来开辟的
提醒:位段涉及很多不确定因素,位段不跨平台,注重可移植的程序应该避免使用位段(本文后面会单独讲)
位段的内存分配
1.数据的存储顺序由是编译器而定(C标准没有规定)
2.每次向CPU申请1byte,如果不够则继续申请1byte
3.是否浪费空间取决于编译器(C标准没有规定)
解析
未存储数据时:
分类讨论:
若按浪费空间处理
由低地址向高地址存储,设数据的二进制的存储顺序为从右向左(VS2022满足此情况)
十进制10==二进制1010 十进制12==二进制1100 十进制3==二进制11 十进制4==二进制100
char a : 3;-->a占3位,1010会被截断为低3位010存储
char b : 4;-->b占4位,恰好可以存储1100
char c : 5;-->c占5位,11占2位,因此补前导零存储为00011
char d : 4;-->d占4位,100占3位,因此补前导零存储为0100
按十六进制拆分:0110 0010 0000 0011 0000 0100-->62 03 04
位段的空间以4个字节(int)或者1个字节(char)的方式来开辟的,a,b,c,d的类型全为char,因此不用改动为62 03 04 00
验证
VS2022+x64+debug环境下,VS2022调试,F11逐语句
打开内存窗口,输入&s(小写的s)
10.位段跨平台问题
1.int 位段被当成有符号数还是无符号数是不确定的(即最高位为1还是0不确定)
2.位段中最大位的数目不能确定
例如:int a : 34;
a为34位,在16位和32位机器均不能存储,但在64位机器可以
3.位段中的成员在内存中的存储顺序为从左向右还是从右向左,C标准尚未定义
4.当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,不确定
因此跟结构相比,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在
11.位段的应用
下图是网络协议中IP数据报的格式,可以看到其中很多的属性只需要几个bit位就能描述
这里使用位段能节省了空间,这样网络传输的数据报大小也会较小一些,对网络的畅通是有帮助的。
12.其他注意事项
位段的几个成员共有同一个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位置处是没有地址的(内存中每个字节分配一个地址,一个字节内部的bit位是没有地址的)
所以不能对位段的成员使用&操作符,这样就不能使用scanf直接给位段的成员输入值,只能是先输入放在一个变量中,然后赋值给位段的成员
int a=0;
scanf("%d",&a);
s.a = a;