目录
结构是⼀些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量,如: 标量、数组、指针,甚⾄是其他结构体。
结构体的声明
// 假如需要描述一个学生,那么需要char类型的名字;int类型的年龄等等
struct Stu
{
char name[20];//名字int age;//年龄
char sex[5];//性别
char id[20];//学号
};//分号不能掉
结构体的初始化和声明
因为生成变量的时候需要进行初始化,所以我们不为结构体成员赋值。而是进行初始化。
#include <stdio.h>
#define NAME_LEN 64struct student
{
char name[NAME_LEN];
int age;
char sex[20];
char id[20];
};int main()
{
struct student chen = { "Chen",20,"男","2021" };
printf("姓名=%s\n", chen.name);
printf("年龄=%d\n", chen.age);
printf("性别=%s\n", chen.sex);
printf("学号=%s\n", chen.id);
return 0;
}
运行结果如下图所示:
注意
声明结构体时所赋的初始值的形式是逗号,将各个结构体成员的初始值依次排列在{ }里面,并使用逗号分割。未赋初始值的成员被初始化为0.
.操作符和->操作符
句点操作符
在上图所示代码当中,我门使用了.操作符来访问结构体对象。该操作符被称为句点操作符。
->操作符
计算结构体的大小
要计算结构体的大小,首先得掌握结构体内存对齐的规则。
- 结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处
- 其他成员变量要对⻬到某个数字(对⻬数)的整数倍的地址处。
对齐数 = 编译器默认的⼀个对齐数与该成员变量大小的较小值。(VS默认对齐数是8)
- 结构体总大小为最大对齐数(结构体中每个成员变量都有⼀个对齐数,所有对齐数中最大的)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体成员对齐到⾃⼰的成员中最大对齐数的整数倍处,结构 体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对⻬数)的整数倍。
示例如下图:
这两个结构体,里面的成员类型是一样的。但大小却不相同。原因如下图所示。
根据上面的规则充分的解释,这张图的来源。首先结构体的第一个成员需要对齐到偏移量为0的位置,所以S1和S2都是从零开始,然后根据第二条规则,对齐数需要为整数倍,那么int类型的大小是4,而默认值为8,所以需要S1要浪费掉3个字节,从4开始往下存储4个字节;而S2只需要接着往下存储一个空间即可。最后根据结构体结构体总大小为最大对齐数的整数倍,S1的最大对齐数是4,现在只占了9个字节所以还得浪费3个字节,所以总大小才是12;而S2则是正好8个字节。所以,在安排结构体成员的时候也需要调整位置。
为什么存在结构体对齐
- 平台问题:不是所有硬件平台都能访问任意地址上的任意数据。
- 性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问,而对齐的内存仅需要一次。
- 总体来说:结构体的内存对齐是拿空间换取时间的做法。
结构体传参
在示例代码中,print2函数要更好一些。原因是,函数在传参的时候,参数需要压栈,会有时间和空间上的系统开销。如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销较大,会导致性能的下降。
结论
结构体传参的时候,要传结构体的地址。
结构体实现位段
这个A就是一个位段类型。
位段的内存分配
- 位段的成员可以是int、unsigned int、signed int或者char等类型
- 位段的空间是按照需要以4个字节(int)或者1个字节(cahr)的方式来开辟的
- 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避使用位段。
总结
跟结构相比,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。