C/C++之自定义类型(结构体,位段,联合体,枚举)详解

news2024/11/15 10:42:44

个人主页:点我进入主页

专栏分类:C语言初阶      C语言程序设计————KTV       C语言小游戏     C语言进阶

C语言刷题

欢迎大家点赞,评论,收藏。

一起努力,一起奔赴大厂。

目录

个人主页:点我进入主页

 

1.前言

2.结构体

2.1结构体声明

2.2结构体初始化

2.3结构体的自引用

2,4结构体的内存对齐

 3.位段

3.1什么是位段

3.2位段的内存分配

3.3位段的跨平台性

4.枚举 

4.1枚举声明

4.2枚举的优点

4.3枚举的使用

5.联合体

5,1联合体的声明

5.2联合体的大小

5.3联合体的使用


 

1.前言

        随着我们深入学习C语言,我们发现单纯的int,char,double,float类型已经不能满足我们的需要了,那C语言是否还有其他的类型呢,事实上还有一类那就是结构体,结构体是我们自己创造的一种类型,它可以包含C语言的所有类型,结构体是什么呢?结构体如何创建?结构体如何初始化?等问题我会给大家详细解析

2.结构体

2.1结构体声明

        对于结构体如何声明,例如我们想创建一个关于学生的信息,包括名字和学号我们可以如下操作:

struct student{
    int num;
    cahr name[50];
};

2.2结构体初始化

        对于结构体的初始化我们可以看如下代码:

#include <stdio.h>
struct student {
	int num;
	char name[50];
};
int main()
{
	struct student s[3] = { {1,"zhansan"},{2,"lisi"} };
	int i;
	for (i = 0; i < 2; i++)
	{
		printf("%d %s\n", s[i].num, s[i].name);
	}
	return 0;
}

        对于结构体的访问我们需要用到“.”或者"->"进行访问“.”就是让面的操作对于“->”就是传址也就是指针我们可以进行如下操作,代码如下:

#include <stdio.h>
struct student {
	int num;
	char name[50];
};
int main()
{
	struct student s[3] = { {1,"zhansan"},{2,"lisi"} },*p=s;
	int i;

	for (i = 0; i < 2; i++)
	{
		printf("%d %s\n", p->num ,p->name );
		p++;
	}
	return 0;
}

2.3结构体的自引用

        对于结构体,还有一种操作就是结构体的自引用,我们还可以理解为结构体嵌套结构体具体的代码如下:

struct student {
	int num;
	char name[50];
};
struct Std {
	struct student std[3];
	int gard;
};

        对于striuct Std类型的变量初始化和struct student类型的相似只是多次操作即可例如s.std[0].num=1;

2,4结构体的内存对齐

        结构体中有一个很有意思的现象,代码如下:

#include <stdio.h>
struct student1 {
	char ch1;
	char ch2;
	int i;
};
struct student2 {
	char ch1;
	int i;
	char ch2;
};
int main()
{
	printf("%d\n", sizeof(struct student1));
	printf("%d\n", sizeof(struct student2));
}

代码输出的结果为

f62c784685694e7c95111a7c38acef0a.png

        问什么会这样呢?我们一般的理解是char占用1个字节,int占4个字节,共占6个字节,这就和结构体的内存对齐有关了 ,首先得掌握结构体的对齐规则:

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

我们可以理解为第一个占0位置,对齐数就是编译器的默认值和成员大小的较小值,偏移量的初始位置为对齐数的倍数,最后所占的字节为最大成员的倍数。 

例如我们第一个结构体进行画图讲解:

 ce1799fbd4da4589b25022f8d8580b5f.png

         ch1占0的位置,ch2的对齐数是1占1的位置,num的对齐数是4占4的位置,共占8个8是4的倍数故占8个字节。

对于对齐数的默认值我们可以用#pragma pack()进行修改,例如#pragma pack(8);

大部分的参考资料都是如是说的:

1. 平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特
定类型的数据,否则抛出硬件异常。
2. 性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访
问。

总体来说:

结构体的内存对齐是拿空间来换取时间的做法。
那在设计结构体的时候,我们既要满足对齐,又要节省空间,如何做到:
让占用空间小的成员尽量集中在一起。

 3.位段

3.1什么是位段

        位段和结构体类似,它的成员是int,unsigned int ,signed int,它的形式是类型 +变量名+: 字节数,它的详细代码可以理解为:

struct num {
	int a : 2;
	int b : 3;
	int i : 30;
};

3.2位段的内存分配

1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

 例如如下代码的内存分配:

#include <stdio.h>
struct S
{
char a:3;
char b:4;
char c:5;
char d:4;
};
int main()
{
	struct S s = { 0 };
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;
	return 0;
}

我们可以进行画图理解:

        由于位段的不确定性所以我我们在一个字节中不知道是占高位还是低位,我们正常思维是占低位,在占低位时可以理解为

0f0d17f8581c4e8a85d6bf95ce61d081.png

        这样第一个字节为01100010为62,第二个字节为00000011为03,第三个字节为00000100为04,真实的储存是不是我们理解的呢?我们进入调试看一看内存

571504a1a14f4bc383e6a33a4b49827d.png

        于是这样就形成了位段,对于位段占几个字节我们可以利用sizeof()进行操作得到它占用几个字节 。

3.3位段的跨平台性

1. int 位段被当成有符号数还是无符号数是不确定的。
2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机
器会出问题。
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是
舍弃剩余的位还是利用,这是不确定的

位段在信息传输时有很重要i的作用,在这里不做讲解。

4.枚举 

4.1枚举声明

enum s {
	blue,
	red,
	back
};

        枚举和#define一样在上面的代码中blue相当于#define blue 0,red相当于 #define red 1,back相当于#define back 2。难道只能从0开始吗?显然是不可能的,我们应该如何修改?如下:

enum s {
	blue=3,
	red=2,
	back=10
};

4.2枚举的优点

我们可以使用 #define 定义常量,为什么非要使用枚举?
枚举的优点:
1. 增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 防止了命名污染(封装)
4. 便于调试
5. 使用方便,一次可以定义多个常量

4.3枚举的使用

枚举的使用主要就是switch case语句中例如

 

enum s {
	blue,
	red,
	back
};
int main()
{
	int a = 1;
	switch (a)
	{
		case blue:
			; break;
		case red:
			; break;
		case back:
			; break;
	}
	return 0;
}

5.联合体

5,1联合体的声明

联合体声明如下:

union Un1
{
	char c[5];
	int i;
};

5.2联合体的大小

联合的大小至少是最大成员的大小。
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

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

对于联合体的成员共同占用一个空间,我们可以做一个测试,代码如下:

#include <stdio.h>
union Un1
{
	char c[5];
	int i;
};

int main()
{
	union Un1 u1;
	printf("%p\n", &u1.c);
	printf("%p\n", &u1.i);
	printf("%p\n", &u1);
}

我们运行结果如下:

cb3aa2523a2c448a981860bdaf716658.png

因此我们可以得到联合体存储的方式

6a359a82e91c4669a91410d2317d0e9b.png

 对于如何计算联合体的大小,我们可以看一下代码:

#include <stdio.h>
union Un1
{
	char c[5];
	int i;
};
union Un2
{
	short c[7];
	int i;
};
int main()
{
	printf("%d\n", sizeof(union Un1));
	printf("%d\n", sizeof(union Un2));
}

对于Un1我们可以画成a726c0b001d242fd9f0614e7d9ae6a79.png

        对于c占5个字节,i占4个字节,但是c是char类型是1个字节,成员最大的为4,由于需要占最大成员的倍数 故占8个字节。Un2也是同样的操作,short占2个字节,共14个字节,int占4个字节,共占用16个字节。

5.3联合体的使用

        我们知道联合体是一种节省空间存储方式,我们可以把它用在多个不共同使用的多i个结构体创建上大致可以理解为

struct num{

        union u1{

               结构体1;

               结构体2;

                ......

        };

};

今天的内容就结束了,欢迎大家来三连。 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1080411.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用 KubeSkoop exporter 监测和定位容器网络抖动问题

作者&#xff1a;遐宇、溪恒 本文是 8 月 17 日直播的文字稿整理&#xff0c;文末可观看直播回放。除去文章内容外&#xff0c;还包括针对实际网络问题的实战环节。 容器网络抖动问题发生频率低&#xff0c;时间短&#xff0c;是网络问题中最难定位和解决的问题之一。 不仅如…

Java基于SpringBoot的学生就业管理信息系统

文章目录 1. 简介2. 技术栈3. 总体设计4 系统设计4.1前台功能模块4.2后台功能模块4.2.1管理员功能 六 源码咨询 1. 简介 Java基于SpringBoot的学生就业管理信息系统&#xff0c;本次设计任务是要设计一个就业信息管理系统&#xff0c;通过这个系统能够满足就业信息管理功能。系…

【接口测试】如何在 Eolink Apilkit 中使用 cookie ?

什么是 Cookie &#xff1f; Cookie是一种在网站之间传递的小型文本文件&#xff0c;用于存储用户的个人信息和偏好设置。当您访问一个网站时&#xff0c;网站会将Cookie存储在您的浏览器中&#xff0c;并在您下次访问该网站时读取该Cookie。这样&#xff0c;网站可以记住您的…

【Spring AOP】Spring AOP 详解

Spring AOP 详解 一. 什么是 AOP二. AOP 组成切面&#xff08;Aspect&#xff09;连接点&#xff08;Join Point&#xff09;切点&#xff08;Pointcut&#xff09;通知&#xff08;Advice&#xff09; 三. Spring AOP 实现1. 添加 AOP 框架⽀持2. 定义切面和切点3. 定义相关通…

nginx windows安装部署,代理转发配置

一、安装 1、nginx官网下载 windows版本 nginx官网 下载后解压到本地 2、在nginx的配置文件是conf目录下的nginx.conf&#xff0c;默认配置的nginx监听的端口为80&#xff0c;如果本地电脑的80端口有被占用&#xff0c;如果本地80端口已经被使用则修改成其他端口。如下&…

【吴恩达深度学习】

第一周 1、修正线性单元ReLU 第二周、Logistic回归 1、样本矩阵X&#xff1a; 是一个m*nx的矩阵&#xff0c;表示m个样本&#xff08;一个竖列代表一个样本&#xff09;&#xff0c;每个样本有nx个特征。 2、标签矩阵Y&#xff1a;[y1,y2,y3,ym] m个训练样本分别对应的标签…

C++程序员必修第一课【C++基础课程】01:安装C++开发环境

1 本课主要内容&#xff1a; 了解 C 开发相关基础概念学会在 Windows10 上安装 Visual Studio 2019免费社区版新建第一个 "Hello World" C 程序&#xff0c;验证 C 开发环境 2 主要知识点&#xff1a; C 开发环境是同时指操作系统和C开发工具操作系统主要有 Window…

软件开发人员 Kubernetes 入门指南|Part 1

Kubernetes 是一个用于部署和管理容器的编排系统。使用 Kubernetes&#xff0c;用户可以通过自动执行管理任务&#xff08;例如在跨节点间扩展容器并在容器停止时重新启动任务&#xff09;&#xff0c;在不同环境中可靠地运行容器。 Kubernetes 提供的抽象可以让你从 Pod&am…

二维码智慧门牌管理系统:提升社会治理水平,创新市民服务方式

文章目录 前言一、适应市域社会治理现代化二、解决地名地址管理核心问题三、拓展多元化服务 前言 随着科技的不断发展&#xff0c;社会治理的方式和方法也在不断更新和升级。近年来&#xff0c;市域社会治理现代化已成为社会发展的必然趋势&#xff0c;而二维码智慧门牌管理系…

011:获取上证50的所有股票代码,并下载各个股K线数到excel表中

我们结合《获取上证50的所有股票代码》&#xff0c;《根据股票代码和起始日期获取K线数据到excel表》两文中的脚本&#xff0c;搞出新的脚本&#xff1a; import tkinter as tk from tkinter import messagebox from tkcalendar import Calendar import pandas as pd import…

阿里8年经验之谈 —— 如何编写有效的接口测试?

阿里妹导读&#xff1a;在所有的开发测试中&#xff0c;接口测试是必不可少的一项。有效且覆盖完整的接口测试&#xff0c;不仅能保障新功能的开发质量&#xff0c;还能让开发在修改功能逻辑的时候有回归的能力&#xff0c;同时也是能优雅地进行重构的前提。编写接口测试要遵守…

web前端面试-- js深拷贝的一些bug,特殊对象属性(RegExp,Date,Error,Symbol,Function)处理,循环引用weekmap处理

本人是一个web前端开发工程师&#xff0c;主要是vue框架&#xff0c;整理了一些面试题&#xff0c;今后也会一直更新&#xff0c;有好题目的同学欢迎评论区分享 ;-&#xff09; web面试题专栏&#xff1a;点击此处 文章目录 深拷贝和浅拷贝的区别浅拷贝示例深拷贝示例 特殊对象…

LeetCode-145-二叉树的后序遍历

文章目录 递归法非递归法 题目描述&#xff1a; 题目链接&#xff1a;LeetCode-145-二叉树的后序遍历 递归法 解题思路&#xff1a;递归&#xff0c;具体可以参考 LeetCode-144-二叉树的前序遍历 代码实现&#xff1a; class Solution {List<Integer> listnew ArrayList…

数据结构中常见的排序及其代码C语言版本

这里非常抱歉 没有做好动图 等我做好了动态单独做一期博客到时候大家结合动图将本篇博客联系起来看 更容易理解一些喔 常见的数据结构中的排序算法有冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序等。 冒泡排序&#xff1a;比较相邻的元素&#xff0c;…

京东数据分析软件:京东销售数据在哪里看?

京东数据分析可以帮助企业了解京东平台上的销售数据和用户行为数据&#xff0c;以便更有效地制定营销策略和优化经营。例如&#xff0c;通过分析京东销量数据&#xff0c;店家可以了解自己店铺的销售情况&#xff0c;发现问题并及时调整。这对于店家来说具有重要的作用&#xf…

塔望食观察 | 中国海参产业发展现状及挑战

海参&#xff0c;一个古老的物种&#xff0c;堪称海底活化石&#xff0c;据资料显示&#xff0c;海参在地球上存活超过6亿年&#xff0c;比恐龙还早。海参的药用、食疗和营养滋补价值极高&#xff0c;清朝学者赵学敏编的《本草纲目拾遗》有这样的叙述&#xff1a;“海参性温补&…

SpringBoot通过配置切换注册中心(多注册中心nacos和eureka)

场景&#xff1a; 因项目需要&#xff0c;一个springcloud微服务工程需要同时部署到A,B两个项目使用&#xff0c;但A项目使用Eureka注册中心&#xff0c;B项目使用Nacos注册中心&#xff0c;现在需要通过部署时修改配置来实现多注册中心的切换。 解决思路&#xff1a; 如果同时…

算法通关村第17关【黄金】| 跳跃游戏问题

1. 跳跃游戏 思路&#xff1a;每个nums[i]i代表当前最后覆盖范围&#xff0c;这里和字符串分割那道题一样求覆盖区间&#xff0c;取cur的最大值 class Solution {public boolean canJump(int[] nums) {int len nums.length;if(len 1){return true;}int cur 0;for(int i 0;…

如何建立线上线下相结合的数字化新零售体系?

身处今数字化时代&#xff0c;建立线上线下相结合的数字化新零售体系是企业成功的关键。蚓链数字化营销系统致力于帮助企业实现数字化转型&#xff0c;打通线上线下销售渠道&#xff0c;提升品牌影响力和用户黏性&#xff0c;那么具体是如何建立的&#xff1f; 1. 搭建数字化中…

虹科分享 | 确保冻干工艺开发中精确测量和数据完整性的5步指南

虹科分享 | 确保冻干工艺开发中精确测量和数据完整性的5步指南 介绍 冻干周期的工艺开发在冻干中起着至关重要的作用&#xff0c;因为它可以优化关键工艺参数&#xff0c;以实现理想的产品质量和工艺一致性。优化冻干工艺还可以缩短运行时间&#xff0c;尽早发现关键错误&…