[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…...
将对透视变换后的图像使用Otsu进行阈值化,来分离黑色和白色像素。这句话中的Otsu是什么意思?
Otsu 是一种自动阈值化方法,用于将图像分割为前景和背景。它通过最小化图像的类内方差或等价地最大化类间方差来选择最佳阈值。这种方法特别适用于图像的二值化处理,能够自动确定一个阈值,将图像中的像素分为黑色和白色两类。 Otsu 方法的原…...
使用Spring AI和MCP协议构建图片搜索服务
目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式(本地调用) SSE模式(远程调用) 4. 注册工具提…...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...
FFmpeg:Windows系统小白安装及其使用
一、安装 1.访问官网 Download FFmpeg 2.点击版本目录 3.选择版本点击安装 注意这里选择的是【release buids】,注意左上角标题 例如我安装在目录 F:\FFmpeg 4.解压 5.添加环境变量 把你解压后的bin目录(即exe所在文件夹)加入系统变量…...
全面解析数据库:从基础概念到前沿应用
在数字化时代,数据已成为企业和社会发展的核心资产,而数据库作为存储、管理和处理数据的关键工具,在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理,到社交网络的用户数据存储,再到金融行业的交易记录处理&a…...
解析“道作为序位生成器”的核心原理
解析“道作为序位生成器”的核心原理 以下完整展开道函数的零点调控机制,重点解析"道作为序位生成器"的核心原理与实现框架: 一、道函数的零点调控机制 1. 道作为序位生成器 道在认知坐标系$(x_{\text{物}}, y_{\text{意}}, z_{\text{文}}…...
用 Rust 重写 Linux 内核模块实战:迈向安全内核的新篇章
用 Rust 重写 Linux 内核模块实战:迈向安全内核的新篇章 摘要: 操作系统内核的安全性、稳定性至关重要。传统 Linux 内核模块开发长期依赖于 C 语言,受限于 C 语言本身的内存安全和并发安全问题,开发复杂模块极易引入难以…...
游戏开发中常见的战斗数值英文缩写对照表
游戏开发中常见的战斗数值英文缩写对照表 基础属性(Basic Attributes) 缩写英文全称中文释义常见使用场景HPHit Points / Health Points生命值角色生存状态MPMana Points / Magic Points魔法值技能释放资源SPStamina Points体力值动作消耗资源APAction…...
Python环境安装与虚拟环境配置详解
本文档旨在为Python开发者提供一站式的环境安装与虚拟环境配置指南,适用于Windows、macOS和Linux系统。无论你是初学者还是有经验的开发者,都能在此找到适合自己的环境搭建方法和常见问题的解决方案。 快速开始 一分钟快速安装与虚拟环境配置 # macOS/…...
