[C语言]自定义类型详解:结构体、联合体、枚举
目录
🚀结构体
🔥结构体类型的声明
🔥结构的自引用
🔥结构体变量的定义和初始化
🔥结构体内存对齐
🔥结构体传参
🔥结构体实现位段(位段的填充&可移植性)
🚀枚举
🔥枚举类型的定义
🔥枚举的优点
🔥枚举的使用
🚀联合(共用体)
🔥联合联合类型的定义
🔥联合的特点
🔥联合大小的计算
🚀结构体
🔥结构体类型的声明
结构是一些值的集合,这些值成为成员变量。结构的每个成员可以是不同类型的变量。
结构的声明:
struct Stu
{
char name[20];//姓名
int age;//年龄
char sex[5];//性别
char id[20];//学号
};//分号不能省略//struct——>结构体关键字;Stu——>结构体标签这里表示的是学生属性
特殊声明:
//匿名结构体类型
struct
{
int a;
char b;
}x;//匿名结构体类型只能使用一次
struct
{
int a;
char b;
}a[20],* p;//p指向的是这个结构体指针的地址
int main()
{
p = &x;
return 0;
} //编译器会把上面两个声明当成两个完全不同的类型
🔥结构的自引用
在结构体中包含一个类型为该结构本身的成员
struct Node
{
int data;
struct Node next;
};
这种类型的结构自引用是非法的,成员next中又会包含一个struct Node的结构,如此递归下去永无止境。在计算sizeof(struct Node)时无法求出。合法声明:
struct Node
{
int data;
struct Node* next;
};//也就是说线性结构中链表,每个节点包括了这个节点的数据和指向下一节点的地址的指针的信息。即数据域和指针域。
当使用typedef类型定义和自引用时要注意下面这种陷阱:
typedef struct
{
int data;
Node* next
}Node;//匿名结构体类型,typedef重定义一个名字Node
这种写法是非法的,因为类型名知道整个定义结束才遇到,在结构体内部的Node是未定义的
//解决方案:
typedef struct Node
{
int data;
struct Node* next
}Node;
🔥结构体变量的定义和初始化
struct Point
{
int x;
int y;
}s1; //声明类型的同时定义变量s1
struct Point s2;//定义结构体变量s2
//结构体嵌套初始化
struct Score
{
int n;
char ch
};
struct Stu
{
char name[20];
int age;
struct Score s
};
int main()
{
struct Stu s1 = { "zhangsan",20,{20,'q'} };
}
🔥结构体内存对齐
如何来计算结构体的大小
#include<stdio.h>
struct S1
{char a;int i;char b;
};
struct S2
{char a;char b;int i;
};
int main()
{printf("%d\n", sizeof(struct S1));printf("%d\n", sizeof(struct S2));return 0;
}
明明只是调换了一下位置,为什么所占字节大小会不同呢?
结构体的对齐规则:
1、第一个成员在与结构体变量偏移量为0的地址处
2、其他成员变量要对齐到某个数字(对其数)的整数倍的地址处。
对其数=编译器默认的一个对其数与该成员大小的较小值。
vs中默认的值是8
3、结构体总体大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。
4、如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
为什么会存在结构体内存?
1、平台原因:
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则会抛出硬件异常。
2、性能原因:
数据结构(尤其是栈)应尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存访问仅需要一次访问。
结构体的内存对齐是拿空间来换取时间的做法。
设计结构体的时候,我们既要满足对齐又要满足节省空间,尽量把小的类型集中在前面,从而减少空间的浪费
修改默认对其数
#pragma pack(8)//设置默认对齐数为8
struct S1
{
char a;
int i;
char b;
};
#pragma pack()//取消设置的默认对齐数,还原为默认#pragma pack(1)//设置默认对齐数为1
struct S2
{
char a;
int i;
char b;
};
建议不要随意修改,当我们不去追求效率,而是追求空间浪费最少时可以考虑修改默认对齐数。
🔥结构体传参
#include<stdio.h>
struct S
{int data[100];int num;
};
void print1(struct S ss)
{int i = 0;for (i = 0; i < 2; i++){printf("%d ", ss.data[i]);}printf("%d", ss.num);
}
void print2(struct S* ss)
{int i = 0;for (i = 0; i < 2; i++){printf("%d ", ss->data[i]);}printf("%d", ss->num);
}
int main()
{struct S s = { {1,2},100 };print1(s);//传值调用print2(&s);//传址调用return 0;
}
在进行结构体传参时我们传地址是更好的,这是因为函数传参的时候,参数是需要压栈的,会有时间和空间上的系统开销。如果传递一个结构体对象的时候,结构以过大,参数压栈的系统开销比较大,会导致性能的下降。
🔥结构体实现位段(位段的填充&可移植性)
位段(位指的是比特位)的声明和结构是类似的,有两个不同:
1、位段的成员只能是整型:int、unsigned int,signed int或者char类型的
2、位段的成员后面有一个冒号和数字
#include<stdio.h>
struct A
{int a : 2;int b : 5;int c : 10;int d : 30;
};
位段的内存分配
1、位段成员可以是int 、unsigned int、signed int或者char类型
2、位段的空间上是按照需要以4(int)个字节或 1(char)个字节的方式来开辟的
3、位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应避免使用位段
//举个栗子:
#include<stdio.h>
struct S
{
//先开辟一个字节的空间,8个比特位
char a : 3;
//用了3个还剩5个比特位
char b : 4;
//用了4个还剩1个比特位
char c : 5;
//不够用了,再开辟一个字节,8个比特位,用了5个,剩下3个
char d : 4;
//又不够用了,再开辟一个字节,8个比特位用了4个,剩下4个
};
int main()
{
struct S s = { 0 };
printf("%d\n", sizeof(struct S));//3
s.a = 10;
s.b = 12;
s.c = 3;
s.d = 4;
return 0;
}
调试一走,我们发现vs的规则和我们计算的是没有差别的
但我们在使用位段的时候会有很多问题:
1、int位段被当成有符号数还是无符号数是不确定的
2、位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。
3、位段中的成员在内存中从左向右分配,还是从右向左分配标准未定义。
4、当一个结构中包含两个位段,第二个位段的成员比较大,无法容纳容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的
总结:与结构相比,位段可以达到同样效果,但是可以很好的节省空间,但是有跨平台的问题存在。 当然位段在计算机网络中有其独有的作用,能节省不少空间浪费(数据越少,状态越好),从而达到网络环境较优的状态
🚀枚举
枚举顾名思义就是一一列举,比如:一年12个月可以一一列举、一周7天可以一一列举。
🔥枚举类型的定义
enum Day//星期
{
Mon,
Tues,
Wed
Thur,
Fri,
Sat,
Sun
};enum Sex//性别
{
MALE,
FEMALE,
SECRET
};//与结构体是非常相似的,但其内部是用逗号分隔开的,且内部只包含符号
{ }里面的内容是枚举类型的可能取值,也叫枚举常量。
这些可能取值都是有值的,默认从0开始,一次递增一,当然在定义的时候也可以赋初值。
enum Color//颜色
{
RED = 1,
GREEN=2,
BLUE=3
};
🔥枚举的优点
我们可以使用 #define 定义常量,为什么要用枚举呢?
1、增加代码的可读性和可维护性
2、和#define定义的标识符比较枚举有类型检查,更加严谨
3、防止命名污染(封装)
4、便于调试
5、使用方便,一次可以定义多个常量
🔥枚举的使用
enum Color//颜色
{RED = 1,GREEN=2,BLUE=4
};
int main()
{enum Color clr = GREEN;//只能拿枚举常量给枚举变量赋值,才不会出现类型的差异clr = 5;return 0;
}
🚀联合(共用体)
🔥联合联合类型的定义
联合也是一种特殊的自定义类型,这种类型定义的变量也包含一系列的成员,特征是这些成员共用同一块空间(所以联合也叫共用体)
//举例:
union Un
{
int a;
char c;
};
int main()
{
union Un u;
printf("%d\n", sizeof(u));//4,说明共用了一份空间
return 0;
}
从这里我们就能看出来a与c共用一份空间
🔥联合的特点
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合体至少得有能力保存最大的那个成员)
union Un
{int a;char c;
};
int main()
{union Un u;u.a = 0x11223344;u.c = 0x55;printf("%x\n", u.a);return 0;
}
判断机器是大端存储还是小端存储(用联合的方法)
int check_sys()
{union Un{int a;char b;}u;u.a = 1;return u.b;
}
int main()
{int ret = check_sys();if (ret == 1){printf("小端\n");}elseprintf("大端\n");return 0;
}
🔥联合大小的计算
·联合的大小至少是最大成员的大小。
·当最大成员大小不是最大对齐数的整数倍时,就要对齐到最大对齐数的整数倍。
union Un1
{
char c[5];
int i;
};//本来应该是5,最大对齐数为4,所以大小为4的倍数即8
相关文章:

[C语言]自定义类型详解:结构体、联合体、枚举
目录 🚀结构体 🔥结构体类型的声明 🔥结构的自引用 🔥结构体变量的定义和初始化 🔥结构体内存对齐 🔥结构体传参 🔥结构体实现位段(位段的填充&可移植性) &a…...

Vue3使用Composition API实现响应式
title: Vue3使用Composition API实现响应式 date: 2024/5/29 下午8:10:24 updated: 2024/5/29 下午8:10:24 categories: 前端开发 tags: Vue3CompositionRefsReactiveWatchLifecycleDebugging 1. 介绍 Composition API是Vue.js 3中新增的一组API,用于在组件中组…...

使用moquette mqtt发布wss服务
文章目录 概要一、制作的ssl证书二、配置wss小结 概要 moquette是一款不错的开源mqtt中间件,github地址:https://github.com/moquette-io/moquette。我们在发布mqtt服务的同时,是可以提供websocket服务器的,有些场景下需要用到&a…...

【笔记】软件架构师要点记录(2)
【笔记】软件架构师要点记录 20240523案例一案例二案例三案例四案例五案例六案例七案例十 20240523 基于前10个架构案例场景,对用到的专业术语进行整理,方便后续查看。 案例一 MVC架构风格组件交互方式 MVC是一种用来构建用户界面时采用的架构设计风格…...

56.野指针和悬空指针
一.野指针 野指针指的是指针指向的地址是未知的(随机的,不正确的地址)。 二.野指针出现的几种情况 1.定义指针未初始化 #include <stdio.h>int main(void) {int *p;*p 1;printf("*p is %d\n",*p); } 正确写法࿱…...

echarts-dataset,graphic,dataZoom, toolbox
dataset数据集配置数据 dataset数据集,也可以完成数据的映射,一般用于一段数据画多个图表 例子: options {tooltip: {},dataset: {source: [["product", "2015", "2016", "2017"],["test&q…...

AI界的“拼夕夕”登场,为上万张GPU寻找新使命
在AI领域,一个全新的竞争者已经悄然登场。 AI行业果真有着近乎颠覆性的魅力! 此次事件之后,AI界也许会迎来新一轮的血雨腥风! AI的潮流到底会怎样流转,天知道。 幻方量化,这家以量化投资闻名的公司&…...

STM32-13-MPU
STM32-01-认识单片机 STM32-02-基础知识 STM32-03-HAL库 STM32-04-时钟树 STM32-05-SYSTEM文件夹 STM32-06-GPIO STM32-07-外部中断 STM32-08-串口 STM32-09-IWDG和WWDG STM32-10-定时器 STM32-11-电容触摸按键 STM32-12-OLED模块 文章目录 STM32-12-MPU1. 内存保护单元MPU1. M…...

(超详细)字符函数和字符串函数【上】
前言 C 语言中对字符和字符串的处理很是频繁,但是 C 语言本身是没有字符串类型的,字符串通常放在 常量字符串 中或者 字符数组 中。 字符串常量 适用于那些对它不做修改的字符串函数 . 1.求字符串长度函数 strlen函数 我们要求一个字符串函数的长度…...

AUS GLOBAL 荣获 Brokersview 颁奖盛典多项殊荣
2024年1月31日在迪拜 Sheikh Zayed Rd - Trade Centre - Trade Centre 1 举行的 Brokersview 颁奖盛典上,AUS GLOBAL(澳洲环球)再次展现了其在金融行业的卓越实力,并荣获多项殊荣。 AUS GLOBAL 作为一家全球领先的金融服务提供商…...

Spring Aop 实现对mapper层入参进行重新赋值
需求描述: 需要对mapper查询的入参的某个属性值进行特殊处理后查询 不影响原来业务且方便扩展维护 1,自定义注解 import java.lang.annotation.*;/*** 针对 mapper层入参 按照一定规则进行特殊处理重新赋值*/ Target(ElementType.METHOD) Retention(Ret…...

朗读亭主要作用有哪些?
朗读亭的主要作用有以下几个方面: 1. 提供朗读服务:朗读亭是一个专门的场所,提供给人们朗读的环境和场地。人们可以在朗读亭中选择自己喜欢的书籍或文章,并通过朗读将其表达出来。这样可以帮助人们提高朗读能力,增强自…...

力扣:226. 翻转二叉树
226. 翻转二叉树 已解答 简单 相关标签 相关企业 给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。 示例 1: 输入:root [4,2,7,1,3,6,9] 输出:[4,7,2,9,6,3,1]示例 2: 输入:…...

深入解析 JSONPath:从入门到精通
码到三十五 : 个人主页 在数据处理和交换领域,JSON已经成为了一种广泛使用的数据格式, 如何有效地查询和操作这些数据也变得越来越重要。在这种情况下,JSONPath 应运而生,成为了一种在JSON数据中定位和提取信息的强大工…...

Python算法设计与分析期末
Python算法设计与分析期末通常涉及对算法基础知识的理解和应用,包括但不限于以下几个方面: 算法基础:了解算法的定义、特性(确定性、有穷性、可行性等)以及算法的分类。 时间复杂度和空间复杂度:学会分析算…...

pg_lakehouse 与 datafusion
原理分析 pg_lakehouse 是 ParadeDB 推出的一个开源插件,支持对多种数据湖里的数据做分析计算。它的出现,使得 Postgres 能够像访问本地数据一样轻松访问 S3 等对象存储,轻松访问 Delta Lake 上的表格,具备数据湖分析能力。 pg_…...

基于51单片机的酒精浓度检测仪的设计
一.硬件方案 硬件部分为利用MQ3气敏传感器测量空气中酒精浓度,并转换为电压信号,经A/D转换器转换成数字信号后传给单片机系统,由单片机及其相应外围电路进行信号的处理,显示酒精浓度值以及超阈值声光报警。电路主要由51单片机最小…...

重生之 SpringBoot3 入门保姆级学习(02、打包部署)
重生之 SpringBoot3 入门保姆级学习(02、打包部署) 1.6 打包插件1.7 测试 jar 包1.8 application.properties 的相关配置 1.6 打包插件 官网链接 https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started.html#getting-starte…...

Java-常用模块
文章目录 日期时间stream流 日期时间 jdk8新的日期时间类 解析和格式化DateTimeFormatter类(线程安全) LocalDateTime类 Instant类 Duration类String time "2013-02-11 11:00:00";DateTimeFormatter dateTimeFormatter DateTimeFormatter.o…...

c++大作业 调整字幕的时间
作业及其需求 有时候人们能够下载一些感兴趣的视频但是发现并没有字幕,到字幕网站上查找到字幕文件,但是发现时间进度上不能完美配合,一个视频数据的例子来源于链接: BBC.巴塔哥尼亚:地球秘密乐园 https://www.aliyundrive.com/s/LmF2sgrQzMu/folder/612af030c6fa4bf4b7c…...

Nmap使用方法
Nmap 介绍 Nmap是一个免费开放的网络扫描和嗅探工具包,也叫网络映射器(Network Mapper)。该工具其基本功能有三个,一是探测一组主机是否在线;其次是扫描主机端口,嗅探所提供的网络服务;三是可…...

任务3.1:采用面向对象方式求三角形面积
面向对象编程(OOP)是一种将现实世界中的实体抽象为对象,并通过类和对象来模拟现实世界中的行为和属性的编程范式。在本实战任务中,我们通过创建一个Triangle类来模拟现实世界中的三角形,并使用面向对象的方法来求解三角…...

解读《互联网政务应用安全管理规定》网络和数据安全中的身份认证和审计合规建设
为保障互联网政务应用安全,由中央网络安全和信息化委员会办公室、中央机构编制委员会办公室、工业和信息化部、公安部制定的《互联网政务应用安全管理规定》近日印发,自2024年7月1日起施行。 规定共8章,包括总则、开办和建设、信息安全、网络…...

HTML-JavaWeb
目录 1.标题排版 2.标题样式 编辑 编辑 小结 3.超链接 4.正文排版 编辑编辑编辑5.正文布局 6.表格标签 7.表单标签 8.表单项标签 1.标题排版 ● 图片标签 :< img> src:指定图像的ur1(绝对路径/相对路径) width:图像的宽度(像素/相对于父元素的百…...

数组-检查数组内是否存在和为7的倍数的子序列
一、题目描述 二、解题思路 这里首先要分辨清楚是子序列还是子数组 原数组:[1,2,3,4,5] 子序列:元素和元素之间相对位置保持不变,但是在原数组中不一定连续,如:[1,3,4]; 子数组:元素元素之间保…...

【图像处理与机器视觉】图像处理概述与像素
什么是数字图像处理 改善图像信息,便于作出解释 方便对图像传输,储存,方便机器理解 什么是数字图像 (1)模拟图像:连续二维函数 f(x,y)表示,其中 x…...

虚函数的性能消耗到底在哪?
虚函数的性能消耗 聊到虚函数的性能开销,大家的第一反应肯定是间接调用上,何为间接调用? 当调用一个虚函数时,实际执行的函数版本是在运行时通过虚函数表(virtualtable)查找确定的。这个查找过程是一个间接…...

Visual Studio 的使用
目录 1. 引言 2. 安装和配置 2.1 系统要求 2.2 安装步骤 2.3 初次配置 3. 界面介绍 3.1 菜单栏和工具栏 3.2 解决方案资源管理器 3.3 编辑器窗口 3.4 输出窗口 3.5 错误列表 3.6 属性窗口 4. 项目管理 4.1 创建新项目 4.2 导入现有项目 4.3 项目属性配置 5. 代…...

Web前端与App前端:深入剖析两者的异同
Web前端与App前端:深入剖析两者的异同 在数字化时代,前端技术已成为连接用户与数字世界的桥梁。然而,当我们谈及前端时,往往会遇到两个相似的概念:Web前端和App前端。这两者是否完全相同,还是各有千秋&…...

初学者必读:Midjourney AI创作工具的简易使用手册!
在数字化时代,AI的应用不断推动着各个领域的发展。在这些领域中,AI在艺术和设计方面的应用引起了广泛的关注。AI绘画软件作为今年的热门,Midjourney 通过其独特的原理和方便的使用方法,为创作者提供了一个全新的创作逼真绘画的平台…...