zl程序教程

您现在的位置是:首页 >  后端

当前栏目

C语言自定义类型详细介绍

C语言 介绍 详细 类型 自定义
2023-09-11 14:16:58 时间

自定义类型

提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加
结构体类型


提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档

一、结构体类型

结构是一些值的集合,这些值可以称之为成员变量,结构体里的每个成员都可以是不同类型的变量。比如描述一个学生那么其成员变量可以为姓名,年龄,学号等。

那么如何定义一个结构体变量呢?如图:

我们在声明结构体的时候,可以进行不完全的声明,比如匿名结构体类型(如上图将student去掉就是匿名结构体类型),需要我们注意的是匿名结构体类型需要在分号前定义结构体类型变量,如下图所示:

 

x就是我们定义的结构体类型变量。那么问题来了,匿名的结构体类型其类型一样吗?如下图:

 

如果你认为这两个结构体里面各成员相同那么类型也相同那就错了,实际上编译器会认为上述两个结构体的类型不一样,如图:

 

那么结构体可以自己包含自己吗?下面这张图的结构体自引用是否正确呢?

 

其结果你也猜到了,连编译器都报错了,答案是并不对, 结构体的自引用的正确写法应该是如下图所示:

那么如何定义结构体变量和初始化呢?如下图所示:

 

此时的p1  p2均为全局变量,那么如何初始化?如下图:

 

当然我们也可以将结构体变量嵌套然后进行初始化,如下图所示:

 

下面我们就来讲解一下结构体内存对齐。只有了解了结构体内存对齐,我们才能正确的计算结构体类型。

如图:

 

一般我们想到的是其大小是每个类型相加,其结果并不对,此结构体大小为12,那么为什么是12呢?原因是结构体对齐,下面讲解一下结构体的对齐规则:

1.第一个成员在与结构体变量偏移量为0的地址处

2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。

对齐数=编译器默认的一个对齐数与该成员大小的较小值

3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

按照这个规则我们来看上面题如何计算大小,如图所示:

 

结构体的第一个变量c1放在了偏移量为0的位置上,而第二个类型是int类型,必须要放在其对齐数的整数倍处,int的对齐数为4,vs默认对齐数为8,所以取小值就是4那么int放在4的位置 ,中间的1 2 3空间只能浪费,而c2的对齐数为1和默认对齐数相比1位小值任何数都是1的倍数所以char类型在任何位置都能存放,放完c2后发现一共占用了9个空间,而结构体总大小为最大对齐数的整数倍,刚刚各个成员变量的最大对齐数为4,而9不是4的整数倍所以需要再浪费3个空间到12所以总大小就是12。

下面讲解一下嵌套结构体类型的大小计算

首先看讲解图:

 

 

由于此结构体内嵌所以我先计算了内嵌的结构体大小,看第一个图发现double为第一个元素在0偏移量 占用8个字节后,char的偏移量为1和vs默认对齐数相比小于默认对齐数,则对齐数为1,8为1的倍数所以直接放在8的位置,下一个元素是int对齐数为4要放到4的整数倍处,12为4的整数倍所以放在12的位置而用完4字节后发现所有空间加起来为16字节并且16是最大对齐数double 8的倍数,则16就是嵌套的结构体的大小,计算完我们从头开始计算struct s4的大小,首先char为第一个元素在0偏移量处,而第二个变量是结构体,根据规则结构体需要满足其在被嵌套的结构体内的最大偏移量的整数倍处,而struct s3的最大偏移量为double类型偏移量为8所以在8的倍数的位置上放置s3,一共占据16个字节来到24字节的地址处,24正好是最后一个变量double的对齐数的整数倍处所以double放在24地址处,然后放完8个字节到31地址处发现整体大小为32正好是所有变量中最大对齐数double 8的倍数,那么空间就是32了。

 

二、枚举类型

枚举类型是C语言中的一种基本类型,当你的数据量足够大的时候,如果用宏定义就不像枚举那样简洁。那么枚举该如何定义呢?如下图所示:

如图所示,student是自己定义的枚举名,zhansgan等名字为枚举元素,这里要说明的是枚举元素默认从0下标开始如图所示:

 

如果给任意枚举类型赋初始值,后面的元素则会从初始值开始,如图所示:

 

枚举变量的定义可以在定义枚举类型时定义 也可以在重新定义,如图所示:

用枚举变量赋值这能赋值给枚举变量,如图所示:

 

枚举类型既然是连续的那么是否可以像数组那样遍历呢?答案是连续的枚举类型可以进行遍历,如果有枚举元素被重新赋值则不可以进行遍历。

 

二、联合体类型

.

联合体是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,联合体的特征就是这些成员会共用一块空间,所以联合体也被称为共用体。下面是联合体的声明:


s1  和  s2是定义的两个联合体变量。

 

我们发现联合体内成员的地址果然是一样的使用同一块空间。

联合体的大小该怎么计算呢?以下有两个规则:

1.联合的大小最少是最大成员的大小,这样才能包括最大成员。

2.当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍,不知道对齐数的概念可看上面结构体中讲解的对齐数概念。

 

如图所示,联合体内c成员是char类型数组有5个元素则大小就是5,而int i的大小为4,联合体大小为最大成员大小所以是5,由于5不是最大对齐数的整数倍,所以需要再浪费3个自己凑成8而8就是最大对齐数int 4的倍数,所以最后的结果是8。

下面在讲解一下如何用联合体来判断编译器是大端字节序还是小端字节序。

那么为什么可以判断呢?因为联合体空间是最大成员大小也就是int的大小,而char b为1字节所以会将char b放入int开辟的空间中,将int 开辟的空间地址改为0x 12 34 56 78,那么如果char b先使用低字节序78的话就是小端,如果使用12就是大端所以只需要判断b使用的高字节还是低字节就可知道是大端还是小端。