C语言进阶 三、自定义类型
目录
一、结构体
void* 泛型指针
1、结构体的声明
结构是一些值的集合,这些值成为成员变量。结构的每个成员可以是不同类型的变量
数组:一组相同类型元素的集合
//声明结构体
//学生
struct student
{
//学生的相关属性
char name[20];
int age;
};
//声明结构体的同时创建变量
struct student
{
//学生的相关属性
char name[20];
int age;
}s1,s2;
s1和s2是student类型的变量
struct student s3; //单独创建变量
//匿名结构体
//只能用一次
struct
{
//学生的相关属性
char name[20];
int age;
}s1;
2、结构体自引用
数据结构:数据在内存中的存储结构
线性——顺序表、链表
树形——二叉树
顺序表,连续存放
链表,非连续存放
定义链表的一个元素:
struct Node
{
int data;
struct Node next;
}
sizeof(struct Node),编译无法通过,无限套娃,内存无限大
结构体正确的自引用方式
应该按照一下定义
struct Node
{
int data;
struct Node *next; //指针的大小是固定的(32bit机器是4byte)
}
3、结构体变量的定义和初始化
struct Score
{
char level;
int score;
};
struct Stu
{
char name[10];
int age;
struct Score s;
};
void main()
{
struct Score point = {'B',99};
struct Stu student = { "loyzde",20,{'A',1} };
printf("%s %c\n", student.name, student.s.level);
printf("%d\n", sizeof(student));
}
loyzde A
24
4、结构体内存对齐
计算结构体的内存大小
其他编译器没有默认对齐数
offsetof是一个计算偏移量的宏
为什么存在内存对齐
结构体的对齐就是拿空间换取时间的做法
设计结构体时,既要满足对齐,又要节省空间,可以让占用空间小的成员尽量集中在一起。
修改默认对齐数
#pragma pack(m)
作用区域
#pragma pack()
5、结构体传参
结构体传参的时候,参数是需要压栈的,会有时间上和空间上的系统开销,如果传递一个结构体对象的时候,结构体过大,参数压栈的歘通开销较大,导致性能下降。
因此结构体传参时,要传递结构体的地址。
typedef struct Node
{
int data[1000];
int num;
}Node;
void print1(Node node) //值传递
{
for (int i = 0; i < 4; i++)
{
printf("%d\t", node.data[i]);
}
printf("%d",node.num);
printf("\n");
}
void print2(Node* node) //地址传递
{
for (int i = 0; i < 4; i++)
{
printf("%d\t", node->data[i]);
}
printf("%d",node->num);
printf("\n");
}
void main()
{
Node _node = { {1,2,3,4},10 };
print1(_node);
print2(&_node);
}
1 2 3 4 10
1 2 3 4 10
6、结构体实现位段(位段的填充&可移植性)
(1)位段
int flag; //0 1 只需要一个位即可
位段可以用来节省空间。
(2)位段的内存分配
(3)位段的跨平台问题
(4)位段的应用
二、枚举
枚举常量,列表
只能使用枚举常量给枚举变量赋值
三、共用体
1、共用体定义和特点
成员变量公用一块空间。不是单独的空间,成员不能单独使用
联合变量的大小,至少是最大成员的大小
判断机器大小端存储
2、利用共用体判断大小端
3、共用体的大小
共用体计算大小的时候也存在字节对齐