自定义类型(1)

发布于:2022-12-14 ⋅ 阅读:(419) ⋅ 点赞:(0)

自定义类型适用于描述复杂对象,比如,人,具有性别、姓名、年龄、身高等属性,不能简单的用某一个类型就能将所有属性都描述出来,所以此时自定义类型更加的方便

自定义类型有结构体(struct)、枚举(enum)、联合体(union)

目录

1.结构体的声明

1.1结构的基础知识

1.2结构得声明

 1.3特殊的声明

 1.4结构体的自引用

 1.5结构体变量的定义和初始化

1.6结构体内存对齐

1.7修改默认对齐数

1.8结构体传参


本章重点:

  • 结构体
    • 结构体类型的声明
    • 结构的自引用
    • 结构体变量的定义和初始化
    • 结构体内存对齐
    • 结构体传参
    • 结构体实现位端(位段的填充&可移植性)
  • 枚举
    • 枚举类型的定义
    • 枚举的优点
    • 枚举的使用
  • 联合
    • 联合类型的定义
    • 联合的特点
    • 联合大小的计算

1.结构体的声明

1.1结构的基础知识

结构是一些值得集合,这些值称为成员变量。结构得每个成员可以是不同类型得变量

1.2结构得声明

struct tag

{

        member-list;

}variable-list;

 1.3特殊的声明

在声明结构的时候,可以不完全的声明

看下面这样的一个例子

一个是匿名结构体类型变量sb1,另一个是匿名结构体指针变量ps,它们的成员是一样的,那么它们的结构体类型一不一样?如果一样ps=&sb1这个操作能编译成功,我们来运行一下

注意,这里写着从 " * " 到 " * " 的类型不兼容,也就说它们的类型是不相同的

在匿名结构体类型中,当写两个一模一样的成员结构体变量,虽然成员是一模一样的,但是在编译器看来还会认为是两个不同的类型

 1.4结构体的自引用

实现一个链表,就要用到结构体的自引用

数据结构--数据在内存中存储的结构

数据结构有线形数据结构:顺序表、链表;树形数据结构:二叉树

 正确的结构体的自引用:下面这种形式就是链表的定义,data指的是数据域,next是指针域

struct Node
{
    int data;
    struct Node* next;
}

此时的结构体类型就是struct Node;(struct 不能省略掉),对结构体重新命名的时候可以省略掉struct,但是在结构体内部中指针域的名字并不能使用这个重新定义的名字,可以这样理解,重命名之后的名字在我们创建的指针域之后,在创建指针域的时候不能使用新的类型名

 1.5结构体变量的定义和初始化

struct Book
{
	char book_name[20];
	char author[20];
	int price;
	char id[15];
}sb1 = {"C语言程序与设计","谭浩强",55,"THQ10001"},sb2;
int main()
{
	struct Book sb2 = { "数据结构","李春葆","59","LCB10001" };
	printf("%s %s %d %s\n", sb1.book_name, sb1.author, sb1.price, sb1.id);
	printf("%s %s %d %s\n", sb2.book_name, sb2.author, sb2.price, sb2.id);

}

也可以不按照顺序进行初始化

 

1.6结构体内存对齐

计算结构体的大小

一个热门的考点:结构体内存对齐

offsetof宏-->用来计算结构体成员相对于起始位置的偏移量-->头文件为<stddef.h>

 

 

上面为S1的偏移量 

下面为S2的偏移量

 

 结构体的对齐规则:

  • 1.结构体的第一个成员直接对齐到相对于结构体变量起始位置为0的偏移处
  • 2.其他成员变量要对齐到某个数字(对齐数)的整数倍的偏移处
    • 对齐数=编译器默认的一个对齐数与该成员大小的较小值
    • vs中默认的值为8,Linux环境默认不设对齐数,那么Linux的对齐数就是结构体成员自身大小
  • 3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍
  • 4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

 同理可以得到c2的总大小

现在计算以下S3的大小 

这个大小是16个字节

 大小是32字节

先分析s3:

注意这里的s3:这个结构体中最大对齐数是8,所以是结构体s3对齐到8的整数倍,这个结构体的最大的对齐数是8,所以结构体总的大小是8的整数倍,故s3总的大小是16个字节

再分析s4:

c1放到相对于起始位置偏移量为0处,struct s3放到相对于起始偏移量为8的位置,并且这个s3的总大小是16个字节,最后d对齐数是8对齐到8的整数位,得到的结果是32个字节

为什么存在内存对齐?

  • 1.平台原因:

                不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处去某系特定类型的数据,否则抛出硬件异常

  • 2.性能原因:

                数据结构(尤其是栈)应该尽可能地在自然界上对齐

                原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问权仅需要一次访问

总体来说:

结构体的内存对齐是拿空间来换取时间的做法

 那在设计结构体的时候,我们既要满足对齐,又要节省空间:让占用空间小的成员尽量集中在一起

1.7修改默认对齐数

用#pragma这个预处理指令可以改变我们的默认对齐数

 此时默认对齐数是1,所以根据上面的原则这个结构体的大小是6个字节

一般情况下,设置默认对齐数都是2的整数倍

1.8结构体传参

 print1和print2相比print2更好一些

原因:

函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销

如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降

结论:结构体传参的时候,要传结构体的地址

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到