在C语言中,结构体(struct)指的是一种数据结构,是C语言中聚合数据类型(aggregate data type)的一类。结构体可以被声明为变量、指针或数组等,用以实现较复杂的数据结构。结构体同时也是一些元素的集合,这些元素称为结构体的成员(member),且这些成员可以为不同的类型,成员一般用名字访问。
结构体类型创建
结构体的声明使用的是 struct 关键字,它的声明格式如下:
struct tag
{
member-list;
}variable-list;
例如描述一个学生:
struct stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
};//分号不能丢
特殊的说明:
在声明的时候,可以不完全声明,比如:
//不完全声明
struct
{
int a;
char b;
float c;
}x;
它省略掉了结构体标签(tag),但是在之后想要再次建立是就没有办法了,因此不推荐这样的写法,不过我们可以吧一个结构体写成一个类型,使用 typedef 就可以完成,如:
typedef struct
{
int a;
char b;
float c;
}x;
这样x就被重定向为一个类型了,我们可以使用x去定义类型
结构体初始化
结构体的初始化只能在定义变量的同时赋初值,如
struct stu
{
char [20];
int age;
};//结构体声明
struct stu p = {"zhangsan", 18};//初始化
结构体内存对齐
首先掌握一下结构体的几个对齐规则
- 第一个成员在与结构体变量偏移量为0的地址处,
- 变量要对齐到某个数字(对齐数)的整数倍的地址处。
对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 (即:自己的偏移量能整出对齐数)
- VS中默认的值为8
- linux只默认的值是4
- 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
- 如果嵌套了结构体的情况,嵌套的结构体对齐到自己最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
总体来说
结构体的内存对齐就是拿空间换取时间的做法
struct S1
{
char c1;
int i;
char c2;
};
struct S2
{
char c1;
char c2;
int i;
};
S1和S2类型的成员一模一样,但是S1和S2所占空间的大小有了一些区别。
通过sizeof我们可以查看他们的空间大小:
printf("%d\n", sizeof(struct S1));
printf("%d\n", sizeof(struct S2));
结果如下:
我们来分析一下S1(VS环境下)
struct S1
{
char c1;
int i;
char c2;
};
S1 中c1为char类型,类型字节为1个,小于8所以他的对齐数为1,且他的偏移量为0,因此它不需要对齐;
第二个成员i为int型,类型字节为4个,小于8所以它的对齐数为4,它的偏移量这时为1(在c1上面),不能够整除4,因此将 i 移到偏移量为4的地方 ,所以就丢弃了c1到i之间3个字节的空间,且int型自己占4个字节,到这里已经占用了8字节(1+3+4);
同样第三个成员c2的对齐数也是1,这时它的偏移量为9,可以整除它的对齐数。S1的结构已经分析完了,它的所占用9个字节(1+3+4+1)。
最后求的整体的总大小(可不是9),总大小应该为最大对其数的整数倍,S1的最大对齐数应为4,因为9不是4的整数倍,且S1的空间要大于9个字节,因此S1的总大小为12。
图示如下:
位段
- 位段的成员必须是int, unsigned int 或 signed int。
- 位段的成员名后边有一个冒号和一个数字。
struct A
{
int _a : 2;
int _b : 5;
int _c : 10;
int _d : 30;
}
后面的数字分别表示所占bit位的个数
枚举+联合
枚举(enum)就是把可能的取值一一列举。
enum Day
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
联合也是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。
union Un
{
int i;
char c;
};//声明
union Un un;//定义
联合体的特点是公用一块内存空间的,这样一个联合体变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。