IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言第十二课,自定义类型 -> 正文阅读

[C++知识库]C语言第十二课,自定义类型

常见的自定义类型有结构体、枚举、联合体,本篇博客总结了这些自定义类型,方便自己的学习。也希望文档能帮助大家。

结构体

C语言第七课,就讲解了结构体的内容。主要有结构体的声明、结构体的传参、结构体的成员访问。

结构体在数据结构中应用的非常广泛。比如最基本的单链表就可以用如下的结构体表示。

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

在这里插入图片描述
可以发现对于一个数组,如果要对它的元素进行增删改查,就需要对它前后的元素进行移动;而单链表只需要将插入位置相邻的指针,指向一个新的位置就可以了。

结构体的大小

规定:
1.结构体的第一个成员,存放在结构体变量开始位置的0偏移处

2.第二个成员开始,都要对齐到整数倍的地址处对齐数:成员自身大小默认对齐数(8的较小值。

3.结构体的总大小,必须是最大对齐数的整数倍。最大对齐数:成员的对齐数中最大的那个。

3.1.Linux环境下没有默认对齐数。对齐数就是成员自身的大小。

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

举例说明

使用offsetof可以看到每一个成员的偏移量。

例一:
struct S1
{
	char c1;//1 8 1 
	int i;//4 8 4 
	char c2;//1 8 1
	//成员大小  默认对齐数  对齐数
};//12

int main()
{
	printf("%d\n", sizeof(struct S1));//12,空间浪费严重
	//计算偏移量,msdn上面查找
	printf("%d\n", offsetof(struct S1, c1));//0
	printf("%d\n", offsetof(struct S1, i));//4
	printf("%d\n", offsetof(struct S1, c2));//8
	return 0;
}

在这里插入图片描述
由1可知:第一个char类型的变量,放在位置零。
int成员的大小为四,默认对齐数为八,由2可知,要对齐到4的倍数的位置,所以int占的空间为4~7。
第三个char类型的变量只占一个比特位。然而由3可知,最大对齐数是四,所以结构体的大小必须是4的整数倍,所以会浪费三个空间。

例二
struct S2
{
	char c1;//1 8 1
	char c2;//1 8 1
	int i;//4 8 4
	//成员大小  默认对齐数  对齐数
};//8
int main()
{
	printf("%d\n", sizeof(struct S2));//8
	//计算偏移量
	printf("%d\n", offsetof(struct S2, c1));//0
	printf("%d\n", offsetof(struct S2, c2));//1
	printf("%d\n", offsetof(struct S2, i));//4
	return 0;
}

由1可知:第一个char类型的变量,放在位置零。
第二个char类型的变量只占一个比特位。
int成员的大小为四,默认对齐数为八,由2可知,要对齐到4的倍数的位置,所以int占的空间为4~7。
然而由3可知,最大对齐数是四,所以结构体的大小必须是4的整数倍,所以会浪费三个空间。
在这里插入图片描述
对比这两个结构体,我们可以知道将占用空间小的结构体成员放在一起,可以使结构体的大小变小

例三
struct S3
{
	double d;//8 8 8
	char c;//1 8 1
	int i;//4 8 4
	//成员大小  默认对齐数  对齐数
};//16
int main()
{
	printf("%d\n", sizeof(struct S3));//16
	//计算偏移量
	printf("%d\n", offsetof(struct S3, d));//0
	printf("%d\n", offsetof(struct S3, c));//8
	printf("%d\n", offsetof(struct S3, i));//12

	return 0;
}

由1可知:第一个double类型的变量,从位置零开始一直到位置七。
第二个char类型的变量只占一个比特位,由2可知,只用放到一的整数倍处。
int成员的大小为四,默认对齐数为八,由2可知,要对齐到4的倍数的位置,所以int占的空间为12~14。
然而由3可知,最大对齐数是八,所以结构体的大小必须是八的整数倍,所以会浪费一个空间。
在这里插入图片描述

例四
struct S4
{
	char c1;//1 8 1
	struct S3 s3;//16 8 8
	double d;//8 8 8
	//成员大小  默认对齐数  对齐数
};//32
int main()
{
	printf("%d\n", sizeof(struct S4));//32
	//计算偏移量
	printf("%d\n", offsetof(struct S4, c1));//0
	printf("%d\n", offsetof(struct S4, s3));//8
	printf("%d\n", offsetof(struct S4, d));//24
	return 0;
}

由1可知:第一个char类型的变量,从位置零开始。
第二个属于嵌套结构体类型,由4可知:需要对齐到八偏移,从八开始到二十三字节。
最后一个double成员的大小为八,默认对齐数为八,由2可知,要对齐到八的倍数的位置,所以double占的空间为24~31。
在这里插入图片描述

为什么会有内存对齐

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

  2. 性能原因:
    数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
    原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

修改默认对齐数

使用 #pragma 这个预处理指令,可以改变我们的默认对齐数。
把默认对齐数修改一下,改大了没有任何意义,要改小!

#pragma pack(1)///设置默认对齐数为1
struct S1
{
	char c1;
	int i;
	char c2;
};
#pragma pack()///取消设置默认对齐数


int main()
{
	printf("%d\n", sizeof(struct S1));//6
	return 0;
}

位段

位段的声明和结构是类似的,有两个不同:

  1. 位段的成员必须是 int、unsigned int 或signed int 。
  2. 位段的成员名后边有一个冒号和一个数字。

假设表示年龄
int age:10;甚至这个都能够表示0~1023了,很明显如果使用16bit的int存储,任然很浪费内存。
也就是说有些数据不需要使用那么多的空间,那么就采用位段的方式。

struct A
{
	//开辟4byte=32比特位
	int _a : 2;//30剩下      00 01 10 11
	int _b : 5;//25剩下
	int _c : 10;//15剩下
	//开辟4byte -32bit
	int _d : 30;
};
struct S
{
	//开辟1byte-8bit
	char a : 3;//5
	char b : 4;//1
	//1byte-8bit
	char c : 5;//3
	//1byte-8bit
	char d : 4;//
};
int main()
{
	printf("%d\n", sizeof(struct A));//8byte
	printf("%d\n", sizeof(struct S));//3byte
	return 0;
}

在网络上传输一个信息时,比如我要发送一个“多喝热水”,就需要在这个数据上添加发送端的地址和接收端的地址,除此之外还有其他的一些变量。想象一下如果不加上这些东西,我发的消息是不是网上的人都知道了?那不就乱套了吗?
而网络上传输数据的时候,如果数据包过大或数据包太多,就会造成拥堵。那么最好就是要压缩数据,比如标志位只需要三个比特位就够了,我就没有必要创建一个char变量。
在这里插入图片描述
1.在早期的16位机器下,sizeof(int)是16,现在sizeof(int)是32。2.关于机器是大端存储还是小端存储完全取决于编译器。3.第二个位段比较大,无法容纳于第一个位段,开辟一个后,舍弃还是利用第一个位段的剩余空间?
基于这几个方面的问题,体现出了位段的跨平台性不够好。

枚举

枚举类型的定义

枚举顾名思义就是一一列举。
把可能的取值一一列举。
比如我们现实生活中:
一周的星期一到星期日是有限的7天,可以一一列举。

enum Day
{
	//枚举的可能取值
	Mon,
	Tues,
	Wed,
	Thir,
	Fri,
	Sta,
	Sun
};

枚举的使用

枚举变量的默认值从上到下是0 1 2 ……

enum Sex
{
	MALE,//0
	FEMALE,//1
	SECRET//2
};
int main()
{
	printf("%d\n", MALE);//0
	printf("%d\n", FEMALE);//1
	printf("%d\n", SECRET);//2
	return 0;
}

枚举类型是一个常量,如果觉得枚举的值不妥,还可以自己给他赋值。比如下面的代码:
对枚举类型使用typedef定义为一个新的变量Sex。
枚举变量的大小是一个整型。

typedef enum Sex
{
	MALE=1,
	FEMALE=3,
	SECRET=5
}Sex;

int main()
{
	enum Sex s = SECRET;
	Sex s2 = MALE;

	printf("%d\n", sizeof(s2));//枚举类型的大小是4
	printf("%d\n", MALE);//1
	printf("%d\n", FEMALE);//3
	printf("%d\n", SECRET);//5

	return 0;
}

枚举的优点

与 #define 相比,用枚举有什么优点?
枚举的优点:

  1. 增加代码的可读性和可维护性
  2. #define定义的标识符只是做一个替换,并没有表示这个值是什么类型,而枚举是整型。
  3. 防止了命名污染(封装)
  4. 便于调试。#define是不能进行调试的,在预处理的时候,两条代码就合成了一条代码。
  5. 使用方便,一次可以定义多个常量。

做项目的时候就可以感受带枚举的优点。

联合体

使用方法:比如学校的信息管理系统
学生:名字 年龄 身份
老师:名字 年龄 职称
身份和职称就可以作为一个联合体变量

联合类型的定义

联合体有一个更加贴切的名字:共用体。

union Un
{
	char c;//1
	int i;//4
};

int main()
{
	union Un u = {0};
	u.c = 'w';
	u.i = 0x11223344;
	printf("%d\n", sizeof(u));//4
	printf("%p\n", &u);
	printf("%p\n", &(u.c));
	printf("%p\n", &(u.i));//他的两个成员共用一个空间,同一时间上,只能用一个。

	return 0;
}

可以看出它的两个成员的地址都是相同的,也就是说:它的两个成员共用一个空间,同一时间上,只能用一个。
在这里插入图片描述

联合大小的计算

联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联
合至少得有能力保存最大的那个成员)。

当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
比如第一个联合体,默认对齐数为8,最大对齐数是 默认对齐数 和 变量大小 的较小值,所以最大对齐数是4。需要对齐到最大对齐数的整数倍处也就是8的位置。

union Un1
{
    //大小  变量大小  默认对齐数 ->最大对齐数
    char c[5];//5*1  1  8 ->1
    int i;//4   4  8 ->4
};
union Un2
{
    short c[7];//7*2
    int i;//4
};
int main()
{
    printf("%d\n", sizeof(union Un1));//8
    printf("%d\n", sizeof(union Un2));//16
}

再比如第二个联合体,默认对齐数为8,最大对齐数是 默认对齐数 和 变量大小 的较小值,所以最大对齐数是4。需要对齐到最大对齐数的整数倍处也就是16的位置。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-03-21 20:30:30  更:2022-03-21 20:34:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 3:00:08-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码