目录
结构体
定义,初始化,自引用
内存对齐,修改默认对齐数
传参
位段
位段内存问题
跨平台及其应用问题
枚举
定义,初始化
实战举例使用及其优点
联合体
联合体定义
特点
大小计算问题
结构体
定义,初始化,自引用
1:结构体的定义:
结构体是一些值的集合,这些值可以是不同类型的变量,被称为成员变量。
2:初始化:
三种初始化方式,被注释掉的代码只有在支持C99的编译器下才可以使用,他是乱序初始化,我们常用的是顺序初始化。
#include <stdio.h>
struct Student
{
char name[8];
int age;
char sex[4];
}a = {"haha",4,"nan"}; //第一种
int main()
{
struct Student c = { "gaga",6,"?" }; //第二种
//struct Student b = { .age = 5, .name = "hehe", .sex = "nu" }; //第三种
return 0;
}
这里还有一种特殊的声明方式:
这样声明的话只能在这里进行初始化,因为他没有名字,后面再想使用这个结构体,没有名字就无法创建该类型变量。
3:结构体的自引用:
这是错误用例,如果这样写的话,那么sizeof(struct Student)大小是多少呢?这样是算不出来的。
接下来我们看正确用例:
这样,就是一个指针,指向这个结构体类型,就不会出现上面无限套娃的情况,再往后面加其实就是一个链表了。
再看重命名后的错误用例:
Stu前面的代码(typedef是重命名,不算在类型里)都是这个结构体类型,也就是说Stu在结构体里是未定义的,然后去使用Stu,这是错误的,改正:
内存对齐,修改默认对齐数
这里我们将说到结构体的大小计算,涉及到结构体的内存对齐。
我们先看一下这个结构体的大小,各位也可以先计算一下,看看结果是否正确。
正确答案是:12个字节,你算对了吗?
结构体对齐规则:
1:第一个成员变量在与结构体偏移量为0的位置。
2:每个成员变量要对齐到他们各自的对齐数的整数倍处,也就是说偏移量是其对齐数的整数倍,起始偏移量为0,对齐数的大小为其sizeof的大小与默认对齐数比较后取小者。
3:每个编译器都有默认对齐数,VS为8个字节,计算结构体总大小时要看其最大对齐数,取成员变量中最大的对齐数,在每个成员变量各自对齐后,结构体所占内存总大小是将总和补到最大对齐数的整数倍。
4:如果嵌套了结构体,则嵌套的结构体的最大对齐数为其自身的最大对齐数,对齐到自身最大对齐数的整数倍处。
接下来我们来计算这个案例
char ch 占一个字节,VS默认对齐数为8,取小,对齐数为1, 放在开头,int a占4个字节,比8小,对齐数取4,4偏移量是4的倍数,从4位置开始分配四个字节的内存空间,short同理,8是2的整数倍,从该处分配内存空间。
结构体中成员变量的对齐数中最大为4,与默认对齐数8相比取4,最大对齐数为4,总大小为4的倍数,则取12个字节。(图中占用字节为7个字节,浪费5个字节,但这是有意义的)
内存对齐的意义:
1:平台的移植性:不是每个平台都能访问任意位置的地址上的任意数据。
2:性能原因:
总的来说,结构体内存对齐就是用空间换取时间的做法
传参:
结构体传参最好传指针,如果传值,实参的临时拷贝对栈区的压力会增大。
#include <stdio.h>
#include <string.h>
typedef struct T
{
int a;
char b;
}T;
void Test(T* stu)
{
printf("haha:%d", stu->a);
}
int main()
{
T stu;
memset(&stu, 0, sizeof(T));
Test(&stu);
return 0;
}
位段
1:位段成员必须是int ,signed int,unsigned int(char也可以)
2:位段成员名后面有一个冒号和数字(分配的bit位)
A就是一个位段类型。
位段内存问题
位段涉及很多问题,比如读取数据的方向,空间不足时数据存部分还是不存。
位段的空间开辟是按照开辟四个字节(int),或一个字节(char)的方式
跨平台及其应用问题
枚举
定义,初始化
enum A
{
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
使用enum创建枚举类型,从第一个枚举常量开始,若不给定值,默认为0,后面的枚举常量依次加1,我们看结果:
当然,我们也可以给枚举常量赋初值:
实战举例使用及其优点
只能拿枚举常量给枚举变量赋值,才不会出现类型的差异。
当然,硬要强制转换也是可以的,不推荐。
一般来说我们也不这么用,真正要用是在switch语句中使用,每一个case后的常量换成枚举常量。
比如:
case 0:我们可以换成case Add:(假设Add是枚举常量的第一个)
这样我们看起来就会方便一些,不会说一看0,不清楚这是做什么的。
联合体
联合体定义:
一种特殊的自定义类型,接下来看声明:
特点:
所有成员共用同一块空间,他们的起始地址都相同:
一个union联合体的大小,至少是最大成员的大小。
大小计算问题
整体大小也要对齐到最大对齐数的整数倍,上面举例的联合体大小为4个字节。