结构体_联合体_枚举
目录一、结构体1.1 结构体类型的声明1.1.1 结构体声明和初始化1.1.2 typedef重命名结构体结构体自引用规则1.2 结构体内存对齐1.2.1 对齐规则1.2.2 修改默认对齐数1.3 结构体传参1.4 结构体实现位段1.4.1 位段的内存分配1.4.2 位段的跨平台问题1.4.3 位段的应用1.4.4 位段使用的注意事项二、联合体2.1 联合体类型的声明2.2 联合体的内存特点2.2.1 相同成员的结构体和联合体内存对比2.3 联合体大小的计算2.3.1 练习三、枚举3.1 枚举的声明3.2 枚举类型的优点3.3 枚举类型的使用一、结构体1.1 结构体类型的声明含义结构体是C语言的自定义聚合类型用于将多个不同类型的数据打包为一个整体变为一个新的类型。1.1.1 结构体声明和初始化struct 结构体标签名 { 类型名 成员名1; 类型名 成员名2; // ... }; // 末尾分号是语法强制要求不可省略例如描述一个学生// 先声明类型 struct Stu { char name[20]; int age; char sex[5]; }; // 后定义变量 struct Student s1; struct Student s2; // 或者声明变量的同时定义变量 struct Student { char name[20]; int age; char sex[5]; }s1,s2; // 直接在末尾定义变量结构体的特殊声明匿名结构体struct { int a; char b; } x, y; // 仅此处可定义变量结构体成员初始化定义变量时赋值1. 初始化定义变量时赋值//完全初始化 struct Stu s1 {zhangsan, 18}; //部分初始化剩下置0 struct Stu s2 {lisi}; //age 02. 同类型结构体变量直接整体赋值✅struct Stu s1 {zhangsan,18}; struct Stu s3; s3 s1; //整体拷贝把s1全部成员复制给s3注这是拷贝不是共用内存s3 修改不会影响 s1。结构体支持 直接赋值数组不能直接用 整体赋值3. 逐个成员赋值struct Stu s4; s4.age 20; // s4.name wangwu; ❌错误数组名是地址不能直接赋值 strcpy(s4.name, wangwu); //字符串要用strcpy拷贝4.结构体指针访问成员struct Stu s {zhang,18}; struct Stu* ps s; ps-age 22; //指针用 - strcpy(ps-name,lisi);常见错误汇总1.❌ s.name abc;name 是 char 数组char数组在结构体中不能直接赋值必须用strcpy。2.❌ 不同结构体类型不能互相赋值。总结同类型结构体变量可以直接整体赋值完成全部成员拷贝内部字符串数组成员不能直接赋值要用 strcpy。观察下面有趣代码struct { int a; char b; float c; }x; struct { int a; char b; float c; }a[20],*p; // 在上面代码的基础上下面的代码合法吗 p x;警告1.编译器会把上面的两个声明当成完全不同的两个类型所以是非法的。2.匿名结构体类型如果没有进行重命名的话基本只能使用一次。1.1.2 typedef重命名结构体用 typedef 给结构体重命名后续定义变量无需再写 struct 关键字。写法1无标签版不支持自引用typedef struct { char a; int b; } t_t; // t_t 是新类型名 t_t s1; // 直接用别名定义变量写法2带标签版支持自引用链表 / 树必备typedef struct Node { int data; struct Node* next; // 自引用必须使用指针 } t_t; // 新类型名 t_t node1;结构体自引用规则1.不能包含自身类型的普通成员会导致大小无线循环编译错误。2.只能包含指向自身类型的指针。自引用时在匿名结构体自引用时提前用新类型创建成员变量可行吗typedef struct { int data; Node* next; }Node;typedef 重命名的别名要等整个结构体声明完成后才会生效在结构体内部别名还未被定义无法使用。正确自引用结构体必须带有标签不使用匿名结构体。typedef struct Node { int data; struct Node* next; }Node;1.2 结构体内存对齐1.2.1 对齐规则1.结构体的第1个成员对齐到结构体变量起始位置偏移量为0的地址处。2.从第2个成员变量开始都要对齐到某个对齐数的整数倍的地址处。对齐数 编译器默认的一个对齐数 与 该成员变量大小 二者中的较小值。VS的默认的值为 8linux 中 gcc 没有默认的一个对齐数对齐数就是成员自身的大小3.结构体总大小为最大对齐数的整数倍。结构体中每个成员变量都有一个对齐数4.如果嵌套了结构体嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。练习struct S1 { char c1; int i; cahr c2; }; struct S2 { char c1; cahr c2; int i; }; struct S3 { double d; char c; int i; }; struct S4 { char c1; struct S3 s3; double d; }; printf(%zu\n,sizeof(struct S1)); printf(%zu\n,sizeof(struct S2)); printf(%zu\n,sizeof(struct S3)); printf(%zu\n,sizeof(struct S4));为什么结构体存在内存对齐简单说结构体的内存对齐是拿空间来换取时间的做法。在设计结构体时为了既满足对齐又节省空间应该让占用空间小的成员尽量集中在一起。1.2.2 修改默认对齐数利用 #pragma 预处理指令#includestdio.h #pragma pack(1)//设置默认对齐数为1 struct S { char c1; int i; char c2; }; #pragma pack()//取消设置的对齐数还原为默认 int main() { printf(%d\n,sizeof(struct S)); return 0; }1.3 结构体传参#includestdio.h struct S { int arr[5] {1,2,3,4,5}; int num 100; }s; //传值调用 void print1(struct S t) { for(int i 0;i 5;i) { printf(%d ,t.arr[i]); } printf(\n); printf(%d\n,t.num); } //传址调用 void print2(const struct S* ps) { for(int i 0;i 5;i) { printf(%d ,ps-arr[i]); } printf(\n); printf(%d\n,ps-num); } int main() { print1(s); print2(s); return 0; }上述代码中print1 和 print2 哪个更好print2 更好原因如下1.函数传参时参数是需要压栈会有时间和空间上的开销。2.如果传递一个结构题体对象的时候结构体过大参数压栈的系统开销比较大所以会导致性能下降。总结结构体传参的时候要传结构体的地址。1.4 结构体实现位段概念位段也叫位域是 C 语言基于结构体实现的内存精细化控制语法允许手动指定结构体成员占用的二进制位数(bit)核心目的是节省内存常用于硬件寄存器、协议报文等对内存极度敏感的底层场景。位段和结构体的不同1.位段的成员必须是 int、unsigned int 或 signed int 在C99中位段成员的类型也可以选择其他整型家族类型比如char。2.位段的成员名后面有一个冒号和数字。基础语法在结构体成员声明后加位数指定该成员占用的比特数。struct 位段名 { 类型 成员名1 : 位数1; 类型 成员名2 : 位数2; // ... };注1.成员类型通常为 unsigned int、int、unsigned char类型2.位数必须是非负整数不能超过对应类型的总比特数。那么位段所占内存的大小是多少#includestdio.h int main() { struct A { int a:2; int b:5; int c:10; int d:30; }; printf(%zu\n,sizeof(struct A)); return 0; }1.4.1 位段的内存分配1.位段的成员可以是 int、undigned int、char 等类型。2.位段的空间上是按照需要以4个字节int或者1个字节char的方式来开辟。3.位段涉及很多不确定因素位段是不跨平台的注重可移植的程序应该避免使用位段。利用下面代码理解位段内存空间是如何开辟的//⼀个例⼦ struct S { char a:3; char b:4; char c:5; char d:4; }; struct S s {0}; s.a 10; s.b 12; s.c 3; s.d 4; //空间是如何开辟的如图1.4.2 位段的跨平台问题1.int 位段被当成有符号数还是无符号数是不确定的。2.位段中最大的数目是不确定的。16位机器最大1632位机器最大32写成27在16位机器会出问题3.位段中的成员在内存中从左向右分配还是从右向左分配标准尚未定义。4.当一个结构包含两个位段第二个位段成员比较大无法容纳第一个位段剩余的位时是舍弃剩余的位还是利用这是不确定的。总结跟结构体相比位段可以达到相同的效果并且可以很好的节省空间但是有跨平台的问题。1.4.3 位段的应用下图是网络协议中IP数据的格式我们可以看到其中很多的属性只需要几个bit位就能描述这里使用位段能够实现想要的效果也节省了空间这样网络传输的数据报大小也会较小一些对于网络的通畅是有帮助的。1.4.4 位段使用的注意事项1.位段成员不可取地址内存最小寻址单位是字节单个比特没有独立地址因此不能对成员使用 取地址也不能直接用 scanf 读入到位段成员。2.大小不是比特数简单相加位段以存储单元位为单位分配空间剩余空间不足时会废弃并开启新单元最终整体大小为存储单元大小的整数倍不等于所有位段位数之和。3.零长度位段特殊作用unsigned int 0是专属语法作用是强制结束当前存储单元后续位段从下一个全新存储单元开始并非仅空出 0 比特。struct A { int _a : 2; int _b : 5; int _c : 10; int _d : 30; }; int main() { struct A sa {0}; scanf(%d, sa._b);//这是错误的 //正确的⽰范 int b 0; scanf(%d, b); sa._b b; return 0; }二、联合体2.1 联合体类型的声明概念联合体也叫共用体关键字 union像结构体一样也是由一个或者多个成员构成这些成员可以是不同类型。基础语法union 联合体标签名 { 类型 成员名1; 类型 成员名2; // ... }; // 末尾分号为语法强制要求不可省略仅声明类型时不占用内存定义变量时才分配实际内存空间。四种常见的声明方式1. 先声明类型后定义变量union Data { char c; int i; double d; }; union Data d1, d2;2. 声明类型的同时定义变量union Data { char c; int i; double d; } d1, d2;3. 匿名联合体union { char c; int i; } d1;4. typedef 重命名typedef union { char c; int i; double d; } UData; UData d1; // 定义变量无需再写 union 关键字1. 定义时初始化❌不能直接初始化其他成员只能对第一个成员初始化union Un u1 {0x01}; //等价 u1.c 0x01; union Un u {.i100}; //C99指定初始化可以考试很少见结构体与联合体在赋值方面对比✅相同点1.同类型变量都支持直接 变量2 变量1 整体赋值完整拷贝全部内存字节。2.普通变量用.指针用 - 访问成员。3.如果内部包含char[]数组数组不能直接赋值要用 strcpy。❌核心不同结构体各个成员独立内存给其中一个成员赋值不会影响别的成员。2.2 联合体的内存特点内存分配规则1. 联合体总大小 占用空间最大成员的大小因为联合体至少得有能力保存最大的成员并且联合体总体大小必须是所有成员中最大对齐数 的整数倍。2. 起始地址一致所有成员都从联合体的首地址开始存储共享同一块内存起点。观察内存#includestdio.h union UN { char c; int i; }; int main() { union UN un {0}; un.i 0x11223344; un.c 0x55; printf(%x\n,un.i); return 0; }上述代码发现将i的第四个字节的内容修改为了55un的内存局部图如下观察地址#includestdio.h union UN { char a; int b; }; int main() { union UN un { 0 }; printf(un %p\n,un); printf((un.a) %p\n,(un.a)); printf((un.b) %p\n,(un.b)); return 0; }观察发现上述代码三者的地址完全相同。2.2.1 相同成员的结构体和联合体内存对比//结构体 struct S { char c; int i; }; struct S s {0}; //联合体 union Un { char c; int i; }; union Un un {0};内存对比图2.3 联合体大小的计算1.联合体的大小至少是最大成员的大小。2.当最大成员大小不是最大对齐数的整数倍的时候就要对齐到最大对齐数的整数倍。#includestdio.h union Un1 { char c[5]; int i; }; union Un2 { short c[7]; int i; }; int main() { printf(%zu\n,sizeof(union Un1)); printf(%zu\n,sizeof(union Un2)); return 0; }2.3.1 练习1. 某商场搞促销活动要上线一个礼品兑换券礼品兑换单中有三种商品图书、杯子、衬衫。每种商品信息如下图书书名、作者、页数杯子设计衬衫设计、可选颜色、可选尺寸利用联合体归纳上述信息并且使得所使用的空间尽可能的小struct gift_list { int stock_number;//库存量 double price; //定价 int item_type;//商品类型 union { struct { char title[20];//书名 char author[20];//作者 int num_pages;//⻚数 }book; struct { char design[30];//设计 }mug; struct { char design[30];//设计 int colors;//颜⾊ int sizes;//尺⼨ }shirt; }item; };2. 利用联合体判断当前机器是大端还是小端。#includestdio.h union Un { char a; int b; }; int check_sys() { union Un un; un.b 1; return un.a; } int main() { if(check_sys() 1) { printf(小端\n); }else { printf(大端\n); } return 0; }优化版int check_sys() { union { char a; int b; }un; un.b 1; return un.a; }三、枚举含义把可能的取值进行一一列举。3.1 枚举的声明enum 枚举名 { 枚举常量1, 枚举常量2, 枚举常量3 };3.2 枚举类型的优点我们可以使用 #define 定义常量为什么非要使用枚举枚举的优点1.增加代码的可读性和可维护性。2.和 #define 定义的标识符比较枚举有类型检查更加严谨。3.便于调试预处理阶段会删除 #define 定义符号。4.使用方便一次可以定义多个变量。5.枚举常量是遵循作用域规则的枚举声明在函数内只能在函数内使用。3.3 枚举类型的使用enum Color { RED 1, GREED 4, BLUE 8 }; enum Color clr GREED;注在C语言中是可以拿整数给变量赋值的但是在C中不行C的类型检查比较严格。最后感谢阅读欢迎大家交流与指正。

相关新闻

离散与流程制造可参考的生产管理系统有哪些?

离散与流程制造可参考的生产管理系统有哪些?

离散与流程制造可参考的生产管理系统有哪些? 本文要点:离散制造和流程制造对生产管理系统的需求差异巨大——离散制造管的是"零件怎么组装成产品",流程制造管的是"原料怎么转化成分批产品"。本文以轻流在不同制造模式下…

2026/10/12 9:26:50 阅读更多 →
第二十六章 个体判断工程

第二十六章 个体判断工程

第二十六章 个体判断工程📅 2026年08月14日👤 东塬一老翁📂 第三卷 个体元素关系工程第二十六章个体判断工程——Individual Judgment Model26.1 个体判断的本质个体理解解决的是:“这个对象对我意味着什么?”个体判断…

2026/10/12 9:26:20 阅读更多 →
Linux C/C++开发中解决ld链接器找不到库文件的四种方法

Linux C/C++开发中解决ld链接器找不到库文件的四种方法

1. 项目概述:当链接器对你“Say No”时在Linux环境下搞C/C开发,编译环节几乎是家常便饭。make命令作为构建流程的指挥官,其背后是编译器(如gcc)和链接器(如ld)的精密协作。相信不少朋友都遇到过…

2026/9/24 3:41:25 阅读更多 →

最新新闻

SAML 2.0 SSO单点登录实战:从协议原理到对接避坑指南

SAML 2.0 SSO单点登录实战:从协议原理到对接避坑指南

简介:基于SAML 2.0的SSO单点登录实现资料包,面向需要搭建统一身份认证系统的.NET开发人员、架构师,也适合对SAML协议交互流程感兴趣的Java开发者。资源聚焦客户端发送SAML请求、SSO响应验证客户端是否可信任、可信响应返回、加密SAML、客户端…

2026/10/12 9:26:00 阅读更多 →
车载颗粒物测量实战:OBS-ONE PM系统安装、测试与维护经验

车载颗粒物测量实战:OBS-ONE PM系统安装、测试与维护经验

第一次把颗粒物分析仪从实验室搬上卡车的时候,我心里想的是“把设备固定牢靠、管路接好,剩下的就是跑路、采数据”。等真正跑完一个完整的外场项目,我才发现OBS-ONE PM车载废气测量系统最难的部分从来不是仪器本身的精度,而是如何…

2026/10/12 9:26:00 阅读更多 →
FOD-A机场异物数据集实战:从数据转换到YOLO训练全指南

FOD-A机场异物数据集实战:从数据转换到YOLO训练全指南

简介:面向目标检测与计算机视觉研究者的FOD-A机场异物数据集,聚焦机场跑道常见异物碎片(FOD)检测,为缺乏公开标准数据的外物入侵检测任务提供训练与评测基础,也可支撑民航、机场场务等工业场景的智能巡检方…

2026/10/12 9:26:00 阅读更多 →
用Redis ZSet实现支付渠道实时成功率统计与秒级告警

用Redis ZSet实现支付渠道实时成功率统计与秒级告警

做支付网关的同学应该都有这种经历:渠道真的挂掉的那一刻,系统不一定有感知,反而是从投诉和工单里才发现“哦,刚才某个渠道成功率掉了一半”。我之前在某个网关项目里被这个事折腾过好几次,后来把统计从定时扫库换成了…

2026/10/12 9:26:00 阅读更多 →
基于Redis ZSet的支付渠道实时成功率监控方案

基于Redis ZSet的支付渠道实时成功率监控方案

1. 项目要解决的核心问题与整体设计思路1.1 支付渠道监控到底在监控什么做支付系统的同学应该都有过这种经历:凌晨两点被告警电话叫醒,说某某渠道成功率跌到 70% 了,打开后台一看,数据又恢复了,留下一个"查无实据…

2026/10/12 9:26:00 阅读更多 →
稀疏视角新视角合成:VGGT-Diff几何引导视频扩散模型实战

稀疏视角新视角合成:VGGT-Diff几何引导视频扩散模型实战

1. 稀疏视角新视角合成到底难在哪1.1 从“拍几张照片就能环视物体”说起新视角合成(Novel View Synthesis,NVS)这个任务,说人话就是:给你若干张从不同角度拍的照片,让你“脑补”出从其他没拍过的角度看上去…

2026/10/12 9:24:59 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →