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语言——自定义类型


前言

结构体,
枚举,
联合。


提示:以下是本篇文章正文内容,下面案例可供参考

结构体

1、结构体的声明

1.1 结构

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

1.1.2 结构的声明

  • struct关键字
  • 描述一个学生
  • 可以不用初始化
  • 在main外创建的结构体变量是全局变量
  • main里面创建的结构体变量是局部变量
struct Stu	没有Stu的话就是匿名结构体类型
{
	char name[20];	名字
	int age;		年龄 
	char sex[5];	性别
	char id[20];	学号
}s1,s2;	这个是struct Stu 结构体类型的变量
		最后一定要加分号

1.1.3 匿名结构

匿名结构体类型只能使用一次

struct 	没有给标签的话是匿名结构体类型
{
	char name[20];	名字
	int age;		年龄 
	char sex[5];	性别
	char id[20];	学号
}s1,s2;	

int main()
{
	return 0;
}

1.1.4 指针结构

struct 	
{
	char name[20];	名字
	int age;		年龄 
}a[20],* p;	

int main()
{	
	return 0;
}

1.1.5 结构的自引用

  • 结构体的字引用内不能自己包含自己 ?
struct Node
{
	int data;
	struct Node;
};
  • 可以包含一个同类型的结构体指针 √
struct Node
{
	int data;
	struct Node* next;
};
  • 还有这样的重命名是可以的 √
第一种
typedef struct Node
{
 int data;
 struct Node* next;
}*linklist;

第二种
struct Node
{
 int data;
 struct Node* next;
};
typedef struct Node* linklist;

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

  • 定义并赋初值
struct point
{
	int x;
	int y;
}p1 = {2,3};	创建一个p1变量赋初值x = 2,y = 3,
  • 也可以在main里面定义变量赋初值
struct point
{
	int x;
	int y;
}p1 = {2,3};	创建一个p1变量赋初值x = 2,y = 3,
struct Stu
{
	char name[20];
	int age;
};

int main()
{
	struct Point p2 = {3,4}	;
	struct Stu s1 = {“zhangsan",20};	
	return 0;
}
  • 结构体嵌套并打印
struct score
{
	int n;
	char ch;
};
struct Stu
{
	char name[20];
	int age;
	struct score s;
};

int main()
{
	struct Point p2 = {3,4}	;
	struct Stu s1 = {“zhangsan",20,	{100,'q'}};
	嵌套初始化时要加上 {}	
	打印:
	printf("%s %d %d %s\n",s1.name, si.age, s1.s.n, s1.s.ch );	
	return 0;
}

1.3结构体内存对齐

  • 计算结构体大小
  • **偏移量:0是原点到1就偏移量为1 **
  1. 第一个结构体成员在于结构体变量偏移量为0的地址处。
  2. 其他成员变量要对齐到某个数字(对齐数)的整数倍地址处。
    对其数= 编译器默认的一个对其数于该成员大小的较小值
    vs中的默认的值为8
  3. 结构体总大小为最大对齐数的整数倍(每个成员都有一个对齐数)
  4. 如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整
    体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。

  • S1的内存分布
  • 偏移量的分别是0 4 8
    在这里插入图片描述
struct S1
{

	char c1;
	int i;
	char c2;
};
struct S2
{

	char c1;
	char c2;
	int i;
};
int main()
{
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
	return 0;
}


  • offsetof
  • 一个结构体成员在这个类型创建的变量中的偏移量
  • 要引用<stddef.h>头文件
  • S2的内存分布
  • 偏移量是 0 1 8
    在这里插入图片描述
struct S1
{

	char c1;
	int i;
	char c2;
};
struct S2
{

	char c1;
	char c2;
	int i;
};
int main()
{
	printf("%d\n",offsetof(struct S1,c1));	
	printf("%d\n",offsetof(struct S1,i));
	printf("%d\n",offsetof(struct S1,c2));						
	return 0;
}

在这里插入图片描述


嵌套结构体的大小

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

在这里插入图片描述

#include <stddef.h> 

struct S1
{
	double d;
	char c;
	int i;
};
struct S2
{
	char c1;
	struct S1 s1;
	double d;
	
};
int main()
{
	printf("%d\n", offsetof(struct S2, c1));
	printf("%d\n", offsetof(struct S2, s1));
	printf("%d\n", offsetof(struct S2, d));
	printf("%d\n", sizeof(struct S2));
	return 0;
}
  • 总的来说: 结构体的内存对齐是拿空间来换取时间的做法。
  • 所以我们在设计结构体时,我们既要满足对齐,又要节省空间。
  • 让占用空间小的成员尽量集中在一起。
  • 例子:
  • 这两个结构体定义的成员类型一样但是所占内存空间就不同
    在这里插入图片描述
struct S1
{
	char c1;
	int i;
	char c2;
};
struct S2
{
	char c1;
	char c2;
	int i;
};
int main()
{
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
	return 0;
}

1.3.1 修改默认对齐数

  • pragma pack(1)设置默认对齐数为1
  • pragma pack()取消设置的默认对齐数,还原为默认

在这里插入图片描述

#pragma pack(1)//设置默认对齐数为1
struct S1
{
 char c1;
 int i;
 char c2;
};
#pragma pack()//取消设置的默认对齐数,还原为默认
int main()
{
	printf("%d\n",sizeof(struct S1));
	return 0;
}

1.4 结构体传参

  • 如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,会导致性能的下降。
  • 首选传址 用指针接收
  • 怕误操作修改加const
struct S
{
	int data[1000];
	int num;

};
void print1(struct S ss)
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%d ",ss.data[i]);
	}
	printf("%d\n", ss.num);
}


void print2(const struct S* ps)
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%d ", ps->data[i]);
	}
	printf("%d\n", ps->num);
}
int main()
{
	struct S s = { {1,2,3},100 };
	print1(s);
	首选传址
	print2(&s);
	return 0;
}

位段

2 什么是位段

  • 位段的声明和结构是类似的,有两个不同
  • 位段的成员必须是int、unsigned int、signed int
  • 位段的成员后边有一个冒号和一个数字

这就是一个位段
后面数字是bit ( 位 )

struct A
{
	int _a:2;
	int _b:5;
	int _c:10;
	int _d:30;
} ;

2.1位段的内存分配

  1. 位段的成员可以是int.unsigned int signed int char(整形家族)类型。
  2. 位段的孔家你上是按照类型4个(int)或者1个(char)字节的方式来开辟的。
  3. 位段是不跨平台的,注重可移植的程序应该避免使用位段。
  • 位段也有内存对齐,对齐是以类型为准
  • 比如一个
    char _a : 5
    char _b : 4
  • 这个需要2个字节来存储
  • 因为char类型只能放下8位,_a和_b有九位
  • 所以第一个字节存放完_a后剩下的位数会空出来再开辟一个存放_b
    在这里插入图片描述
    见上图?
struct A
{
	4byte == 32bit
	int _a : 2;
	int _b : 5;
	int _c : 10;
	用掉17bit
	剩下15bit
	重新开辟
	
	4byte == 32bit
	int _d : 30;
};
//47
int main()
{
	printf("%d\n", sizeof(struct A));
	return 0;
}

第二个例子
在这里插入图片描述

  • 00000000 —— s.a = 10,_a : 3; s.b = 12, _b :4;
  • 00000000 —— 存放s.c = 3 _c : 5
  • 00000000 —— 存放s.d = 4 _d : 4
  • 三个字节每个字节从右往左存放
  • 第一个字节01100010、第二个字节00000010、第三个字节00000100计算的结果和上图一样(十六进制)
  • 解析(配合代码看):
  • 这四个要存放的数字的二进制为1010 — 10、1100 — 12、0011 — 3、0100 — 4
  • 1010只能放3位所以取010,1100能放四位所以结果为01100010
  • 这是第一个字节
  • 0011能放5位 放不够所以补满 00011 剩下三位放不满后一个所以结果是00000011
  • 这是第二个字节
  • 0100要放4位上面一个放不进去所以在开辟一个 00000100
  • 这是第三个字节
struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};
int main()
{
	struct S s = { 0 };
	s.a = 10;	//二进制为1010
	s.b = 12;	//1100
	s.c = 3;	//0011
	s.d = 4;	//0100
	 
	return 0;
}

2.3 位段的跨平台问题

  1. int 位段被当成有符号数还是无符号数是不确定的。
  2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
  4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。
  • 总结:跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在

枚举

枚举顾名思义就是一 一列举

3 枚举类型的定义

enum
默认起始位置为0
可以设置为其他

在这里插入图片描述

enum Day
{
	Mon,
	//MOn = 1,
	Tues,
	Wed,
	Thur,
	Fri,
	Sat,
	Sun
};
int main()
{
	printf("%d\n", Mon);
	printf("%d\n", Tues);
	printf("%d\n", Wed);
	printf("%d\n", Thur);
	printf("%d\n", Fri);
	printf("%d\n", Sat);
	printf("%d\n", Sun);

	return 0;
}

3.1 枚举的使用

enum A
{
	EXit,
	ADD,
};
int main()
{
	switch ()
		{
			case EXit:
				break;
			case ADD:
				break;
			default:			
				break;
		}
}

联合(共用体)

联合也是一种特殊的自定义类型
关键字union

4.1 联合类型的定义

  • 这种类型定义的变量也包含一系列的成员,
    特征是这些成员公用同一块空间(所以联合也叫共用体)

    在这里插入图片描述
union Un
{
	int a;
	char c;
};
int main()
{
	union Un n;
	printf("%d\n", sizeof(n));
	printf("%p\n", &n);
	printf("%p\n", &(n.a));
	printf("%p\n", &(n.c));
	return 0;
}

判断计算机大小端 ( 普通方法 )

//判断当前计算的大小端存储
int check_sys()
{
	int a = 1;
	return* (char*)&a;
}
int main()
{
	int ret = check_sys();
	if (ret == 1)
		printf("小端\n");
	else
		printf("大端\n");
	return 0;
}

判断计算机大小端( 联合体方法 )

int check_sys()
{
	union	//匿名类型:只能用一次
	{
		char c;
		int i;
	}u;
	u.i = 1;
	return u.c;
}
int main()
{
	int ret = check_sys();
	if (ret == 1)
		printf("小端\n");
	else
		printf("大端\n");

	return 0;
}

联合体大小的计算

在这里插入图片描述

  • 为什么会有八个字节呢?
  • 因为联合体也有对齐。
  • 但是char数组用了5个字节剩三个,
  • int类型也用不了,
  • 所以最后是和char共用那五个字节。
union Un
{
	char arr[5];
	int i;
};
int main()
{
	printf("%d\n", sizeof(union Un));
	return 0;
}
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-07-21 21:19:47  更:2022-07-21 21:21:51 
 
开发: 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年5日历 -2024/5/9 21:18:13-

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