目录
一. 结构体的定义声明和初始化
1.1 结构体定义
- 定义: 结构体是值的集合, 与数组不同的是, 这个值可以是不同类型. 这些不同类型的值也被称为成员变量.
struct Test{
int a = 10;
char b = 'x';
float f = 2.18;
};
1.2 结构体的声明方式
- 结构体主要有以下3种声明方式
1.3 结构体的初始化和成员访问
- 结构体访问成员的方式用: 结构体.成员变量
#include <stdio.h>
struct Day
{
int day;
char cty[20];
};
struct Node
{
int data;
char name[20];
struct Day d;
};
int main()
{
struct Node n = { 1, "djl", {3, "深圳"} };
printf("{ %d, \"%s\", { %d, \"%s\" } }", n.data, n.name, n.d.day, n.d.cty);
return 0;
}
二. 结构体自引用与typedef连用
2.1 结构体自引用
- 结构体自引用典型应用就是链表.
2.2 结构体与typedef连用
- 其作用就是将struct Node重新命名为Node(可以是任何名), 从而简化后续该类型结构体的创建.
#include <stdio.h>
typedef struct Node //这里Node可以省略但是并不建议
{
int data;
struct Node* next;
}Node;
int main()
{
Node n; // 没有typedef的话, 需要: struct Node n;
return 0;
}
三. 结构体内存对齐
3.1 为什么需要内存对齐
- 平台移植问题: 不是所有平台都可以任意地址上的任意数据. 某些平台只能在特定地址取特定数据.
- 性能问题: 处理器对未对齐的内存需要访问两次才能完整取出数据, 反之一次即可.(空间换时间)
3.2 内存对齐规则
- 第一个成员在结构体变量偏移量为0的地址处
- 其他成员变量要对齐某个数字(对齐数)的整数倍地址处[对齐数=编译器默认对齐数与该成员大小的较小值]
- 结构体总大小为成员最大对齐数的整数倍(如果有嵌套结构体, 且该结构体中成员对齐数大于外部结构体, 那么它也是外部结构体最大对齐数)
3.3 拓展知识
- 以下两个关于结构体的知识: 一. 默认对齐数设置方法 二. offsetof(结构体类型, 成员变量); 获取成员变量相对结构体的地址偏移量
#include <stdio.h>
#include <stddef.h> //#pragma()需要的头文件
#pragma(4) //设置默认对齐数(vs中默认为4)
struct S
{
int a;
char name[9];
float f;
};
#pragma() //结束标志.
int main()
{
printf("%d", offsetof(struct S, f)); // offsetof(结构体类型, 成员变量);
return 0;
}
3.3 分析案例
- 分析以下两个结构体所占空间大小
#include <stdio.h>
struct S1
{
char a;
int b;
char c;
};
struct S2
{
char a;
char c;
int b;
};
int main()
{
printf("%d\n", sizeof(struct S1)); // 12
printf("%d", sizeof(struct S2)); // 8
return 0;
}
总结: 由于存在内存对齐机制, 由上面这个案例可以看出, 成员变量合理的排布可以优化空间的使用
四. 结构体传参和位段
4.1 结构体传参
- 结构体可以指针传递, 也可以值传递. 但是对于结构体一般推荐指针传递的方式(节省空间)
#include <stdio.h>
struct S
{
int a;
};
void InitS(struct S* tmp) {}; //指针传递 (需要注意的是, 这里用指针接收, 里面访问成员需要使用: 结构体指针->成员变量)
void printfS(struct S tmp) {}; //值传递(这里可能只是打印结构体内容, 值传递可以防止内容被修改, 不过使用const指针也可以做到这点)
int main()
{
struct S s;
InitS(&s);
printfS(s);
return 0;
}
4.2 位段
- 当需数据量十分庞大时, 普通结构体的内存对齐规则就无法满足需求, 需要使用对空间利用率更高的位段. 例如网络协议需要的包头
4.3 案例
- 下面这段代码输出值为多少? (02290000)
总结: 位段每开辟一次空间都要从小端开始存放