当前位置: 首页 > news >正文

[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语言]自定义类型详解:结构体、联合体、枚举

目录 &#x1f680;结构体 &#x1f525;结构体类型的声明 &#x1f525;结构的自引用 &#x1f525;结构体变量的定义和初始化 &#x1f525;结构体内存对齐 &#x1f525;结构体传参 &#x1f525;结构体实现位段&#xff08;位段的填充&可移植性&#xff09; &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&#xff0c;用于在组件中组…...

使用moquette mqtt发布wss服务

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

【笔记】软件架构师要点记录(2)

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

56.野指针和悬空指针

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

echarts-dataset,graphic,dataZoom, toolbox

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

AI界的“拼夕夕”登场,为上万张GPU寻找新使命

在AI领域&#xff0c;一个全新的竞争者已经悄然登场。 AI行业果真有着近乎颠覆性的魅力&#xff01; 此次事件之后&#xff0c;AI界也许会迎来新一轮的血雨腥风&#xff01; AI的潮流到底会怎样流转&#xff0c;天知道。 幻方量化&#xff0c;这家以量化投资闻名的公司&…...

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 语言中对字符和字符串的处理很是频繁&#xff0c;但是 C 语言本身是没有字符串类型的&#xff0c;字符串通常放在 常量字符串 中或者 字符数组 中。 字符串常量 适用于那些对它不做修改的字符串函数 . 1.求字符串长度函数 strlen函数 我们要求一个字符串函数的长度…...

AUS GLOBAL 荣获 Brokersview 颁奖盛典多项殊荣

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

Spring Aop 实现对mapper层入参进行重新赋值

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

朗读亭主要作用有哪些?

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

力扣:226. 翻转二叉树

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

深入解析 JSONPath:从入门到精通

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

Python算法设计与分析期末

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

pg_lakehouse 与 datafusion

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

基于51单片机的酒精浓度检测仪的设计

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

重生之 SpringBoot3 入门保姆级学习(02、打包部署)

重生之 SpringBoot3 入门保姆级学习&#xff08;02、打包部署&#xff09; 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类&#xff08;线程安全&#xff09; 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…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

听写流程自动化实践,轻量级教育辅助

随着智能教育工具的发展&#xff0c;越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式&#xff0c;也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建&#xff0c;…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...