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. 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用。

相关推荐
冯汉栩2 小时前
Swift Control View,Label渐变颜色(源码)
开发语言·ios·swift
白狐_7983 小时前
408 数据结构|线索二叉树两题详解:先序线索化后的空链域 + 中序前驱/后继判断
c语言·数据结构·链表
从小就看凹凸曼^o^3 小时前
Python基础5 - 字典与集合:(1)字典
开发语言·python
GeekZHR3 小时前
C语言指针进阶补充6:动态内存管理、mem系列内存函数、复杂指针声明,一次补齐指针的“三大盲区“
java·c语言·算法·指针
侧耳倾听1113 小时前
java 日志框架简介
java·开发语言
zlinear数据采集卡3 小时前
数据采集卡从入门到精通(9):分辨率与精度——16位卡不等于1/65536的精度
开发语言·数据库·fpga开发·开源·c#
全栈技术负责人3 小时前
解密 MCP(Model Context Protocol):大模型时代的“Type-C”总线与三驾马车架构深度解析
开发语言·ai·架构
丰锋ff4 小时前
1.7在Qt窗口中添加右键菜单
开发语言·qt
Titan20244 小时前
Linux网络学习:套接字、UDP的封装与应用
linux·服务器·开发语言·网络·c++
流浪0014 小时前
Python基础语法(三):函数
开发语言·python