C语言自定义类型

本篇文章主要介绍三种自定义类型,分别是:结构体、联合体、枚举。

一.结构体

1.结构体类型的声明

直接举一个例子:

//一本书
struct s
{
	char name[10];	//名称
	char a;			//作者
	int p;			//价格
};

2.特殊的声明

结构体也可以不写结构体标签,写一个匿名结构体。这种结构体如果不重命名的话,基本就能使用一次。

//还是这个书本的例子
typedef struct 
{
	char name[10];	//名称
	char a;			//作者
	int p;			//价格
}book;

这里对他进行了重命名。

3.结构体的自引用

这个学过链表都不陌生。就是在结构体内在引用一下自身的结构体。看一下例子:

//一个节点
struct Node
{
	int n;
	struct Node* next;
};

4.结构体内存对齐(重点)

结构体内存对齐主要解决的是结构体大小的问题。

4.1对齐规则

首先我们要了解结构体的内存的对齐规则:

  1. 结构体的第⼀个成员对齐到和结构体变量起始位置偏移量为0的地址处;

  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的⼀个对齐数 与 该成员变量大小的较小值;

PS.VS的默认数是8,Linux没有默认数。

  1. 结构体总大小为最大对齐数(结构体中每个成员变量都有⼀个对齐数,所有对齐数中最大的)的 整数倍;

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

举个例子:

struct s
{
	char a;
	int b;
	char c;
};

首先a占据一个位置(规则1);b是int类型,占四个字节,根据规则2,对齐到整数倍的位置,也就是如图所示的蓝色位置;c按照同样的道理放到黄色的位置;最后整个结构体的大小根据规则3来判断,是4的倍数,所以黄色后面有三个位置是空的。

对第4条规则的解释:

struct s
{
	char a;
	int b;
	char c;
};

struct ss
{
	struct s s1; 
	int a;
};

ss中嵌套了一个s,s的对齐数是s中对齐数的最大值也就是4,s在ss中所占空间大小是它本身的大小也就是12。

4.2为什么要进行内存对齐?

从参考资料大致可以得到以下两点:

  1. 平台原因 (移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

  2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用一个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。

总的来说,结构体内存对齐就是拿空间换时间的做法。以后我们如果在某些特定条件下可以使用这个规则来定义结构体。

4.3修改默认对齐数

#pragma 这个预处理指令,可以改变编译器的默认对齐数。

#pragma pack(1)//将默认对齐数修改成1
#pragma pack()//还原回默认对齐数

5.结构体实现位段

这里简单的介绍一下位段:1. 位段的成员必须是 int、unsigned int 或signed int ,在C99中位段成员的类型也可以选择其他类型。 2. 位段的成员名后边有一个冒号和一个数字。

位段的几个成员共有同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位置处是没有地址的。内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的。 所以不能对位段的成员使用&操作符,这样就不能使用scanf直接给位段的成员输入值,只能是先输入 放在一个变量中,然后赋值给位段的成员。

二.联合体

1.联合体的简介

联合体,又名共用体,即所有成员公用一块内存空间。如果冒然给联合体的一个成员赋值会改变其他成员的值。

//对联合体的声明
union Un
{
	char c;
	int i;
};

2.联合体大小计算

1.联合的大小至少是最大成员的大小;

2.当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

三.枚举

1.枚举类型的声明

enum Day//星期
{
	Mon,
	Tues,
	Wed,
	Thur,
	Fri,
	Sat,
	Sun
};

2.枚举的优点

  1. 增加代码的可读性和可维护性;

  2. 和#define定义的标识符比较枚举有类型检查,更加严谨;

  3. 便于调试,预处理阶段会删除 #define 定义的符号;

  4. 使用方便,⼀次可以定义多个常量;

  5. 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用。

相关推荐
2202_754421542 分钟前
生成MPSOC以及ZYNQ的启动文件BOOT.BIN的小软件
java·linux·开发语言
我只会发热9 分钟前
Java SE 与 Java EE:基础与进阶的探索之旅
java·开发语言·java-ee
懷淰メ18 分钟前
PyQt飞机大战游戏(附下载地址)
开发语言·python·qt·游戏·pyqt·游戏开发·pyqt5
hummhumm32 分钟前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
宁静@星空38 分钟前
006-自定义枚举注解
java·开发语言
hummhumm1 小时前
第 28 章 - Go语言 Web 开发入门
java·开发语言·前端·python·sql·golang·前端框架
武子康1 小时前
Java-07 深入浅出 MyBatis - 一对多模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据库·sql·mybatis·springboot
珹洺1 小时前
C语言数据结构——详细讲解 双链表
c语言·开发语言·网络·数据结构·c++·算法·leetcode
每天吃饭的羊1 小时前
python里的数据结构
开发语言·python
码农飞飞1 小时前
详解Rust枚举类型(enum)的用法
开发语言·rust·match·枚举·匹配·内存安全