本章重点
结构体
- 结构体类型的声明
- 结构的自引用
- 结构体变量的定义和初始化
- 结构体内存对齐
- 结构体传参
- 结构体实现位段(位段的填充&可移植性)
枚举
- 枚举类型的定义
- 枚举的优点
- 枚举的使用
联合
- 联合类型的定义
- 联合的特点
- 联合大小的计算
一、结构体的声明
1.1 结构的基础知识
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
1.2 结构的声明
例如描述一个学生的结构体。
1.3 特殊的声明
在声明结构的时候,可以不完全的声明。
上面的两个结构在声明的时候省略掉了结构体标签(tag)。 那么问题来了?
#include<stdio.h> int main() { //下面的代码合法吗? p = &x; return 0; }
警告:编译器会把上面的两个声明当成完全不同的两个类型。 所以是非法的。
1.4 结构的自引用
在结构中包含一个类型为该结构本身的成员是否可以呢?
//代码1 struct Node { int data; struct Node next; }; //可行否?
如果可以,那sizeof(struct Node)是多少? - 不可求
typedef struct//匿名结构体 { int data; Node* next;//使用重命名 }Node;//匿名结构体重命名
这样写代码,可行?- 还没有形成结构体却使用结构体重命名
解决方案:
typedef struct Node { int data; struct Node* next; }Node;
1.5 结构体变量的定义和初始化
struct Point { int x; int y; }p1; //声明类型的同时定义全局变量p1 struct Point p2; //定义结构体变量p2 //初始化:定义变量的同时赋初值。 struct Point p3 = { 1, 2 }; struct Stu //类型声明 { char name[15];//名字 int age; //年龄 }; struct Stu s = { "zhangsan", 20 };//初始化 struct Node { int data; struct Point p; struct Node* next; }n1 = { 10, {4,5}, NULL }; //结构体嵌套初始化 struct Node n2 = { 20, {5, 6}, NULL };//结构体嵌套初始化
1.6 结构体内存对齐
问题:计算结构体的大小。
#include<stdio.h> struct S1 { char c1; int i; char c2; }; int main() { printf("%d\n", sizeof(struct S1)); return 0; }
char类型占1个字节,int类型占4个字节,那结构体的大小是不是就是6个字节呢?
结果却是12,为什么呢? 结构体的大小到底怎么计算呢???
首先来看偏移量的含义
结构体偏移量是指结构体中的成员相对于结构体起始地址的字节偏移量。在C语言中,结构体的每个成员按照定义的顺序依次存储在内存中,每个成员的存储位置相对于结构体的起始地址有一个偏移量。
如何计算这个偏移量呢?
看看刚刚的结构体成员变量的偏移量是多少?
现象:结构体在内存中不是按照顺序连续存放的,是有一定的对齐规则滴
首先得掌握结构体的对齐规则:
1. 第一个成员在相较于结构体变量起始位置偏移量为0的地址处。
2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。
- VS中默认的值为8
- Linux中没有默认对齐数,对齐数就是成员自身的大小
3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
再来分析一下:
【自定义类型引领视觉盛宴】(中):https://developer.aliyun.com/article/1424803