当前位置: 首页 > 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…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现

摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序&#xff0c;以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务&#xff0c;提供稳定高效的数据处理与业务逻辑支持&#xff1b;利用 uniapp 实现跨平台前…...

Element Plus 表单(el-form)中关于正整数输入的校验规则

目录 1 单个正整数输入1.1 模板1.2 校验规则 2 两个正整数输入&#xff08;联动&#xff09;2.1 模板2.2 校验规则2.3 CSS 1 单个正整数输入 1.1 模板 <el-formref"formRef":model"formData":rules"formRules"label-width"150px"…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

Java数值运算常见陷阱与规避方法

整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...

三分算法与DeepSeek辅助证明是单峰函数

前置 单峰函数有唯一的最大值&#xff0c;最大值左侧的数值严格单调递增&#xff0c;最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值&#xff0c;最小值左侧的数值严格单调递减&#xff0c;最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

关于uniapp展示PDF的解决方案

在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项&#xff1a; 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库&#xff1a; npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...