【C语言】结构体与共用体深入解析
在C语言中,结构体(struct
)和共用体(union
)都是用来存储不同类型数据的复合数据类型,它们在程序设计中具有重要的作用。
推荐阅读:操作符详细解说,让你的编程技能更上一层楼
1. 结构体的定义与使用
1.1 结构体的基本概念
结构体(struct
)是C语言中的一种用户自定义的数据类型,它允许用户将不同类型的数据组合成一个单一的复合数据类型。结构体中的每个数据元素称为“成员”或“字段”。每个成员可以是不同类型的数据。
结构体的定义方式如下:
struct StructName {type member1;type member2;type member3;// ...
};
注意:不要忘了最后的==;==
举例:下面结构体 包含num,gender,name三个成员,成员的数据类型分别是int,char,char 数组
struct StructName {int num;char gender;char name[20];
};
1.2 结构体的定义与初始化
定义一个结构体之后,我们可以定义该结构体类型的变量并对其进行初始化。结构体变量的声明方式如下:
struct StructName varName;
结构体的初始化可以通过两种方式进行:静态初始化和动态初始化。
1.2.1 静态初始化
静态初始化是在定义结构体变量时直接赋值。
struct Person {char name[50];int age;
};struct Person person1 = {"John", 25};
1.2.2 动态初始化
动态初始化是通过用户输入或运行时计算来初始化结构体的成员。
struct Person person2;
strcpy(person2.name, "Alice");
person2.age = 30;
1.3 访问结构体成员
结构体成员通过点操作符(.
)来访问。
struct Person person1 = {"John", 25};
printf("Name: %s, Age: %d\n", person1.name, person1.age);
1.4 结构体的数组
结构体的数组允许我们创建多个结构体对象。声明结构体数组的方式与普通数组类似。
struct Person people[3] = {{"John", 25}, {"Alice", 30}, {"Bob", 22}};
访问结构体数组元素时,需要使用数组索引和点操作符:
printf("Name: %s, Age: %d\n", people[0].name, people[0].age);
1.5 结构体作为函数参数
结构体可以作为函数的参数传递。可以通过值传递或者引用传递(指针传递)传递结构体。
1.5.1 结构体值传递
在函数中对结构体进行值传递时,函数接收到结构体的副本,对副本的修改不会影响原结构体。
void printPerson(struct Person p) {printf("Name: %s, Age: %d\n", p.name, p.age);
}struct Person person1 = {"John", 25};
printPerson(person1);
1.5.2 结构体指针传递
如果希望在函数内修改结构体的内容,可以通过传递结构体指针来引用原结构体。
void updatePerson(struct Person *p) {p->age = 28; // 使用箭头操作符访问结构体指针成员
}struct Person person1 = {"John", 25};
updatePerson(&person1);
printf("Updated Age: %d\n", person1.age);
1.6 结构体内存对齐与填充
结构体在内存中的存储方式受到内存对齐的影响。为了提高处理器的效率,结构体的成员通常会根据其类型进行内存对齐。这意味着有时结构体成员之间可能会有空洞,称为“填充”。
可以使用#pragma pack
指令或__attribute__((packed))
来调整结构体的内存对齐方式。
#pragma pack(push, 1)
struct Example {char a;int b;
};
#pragma pack(pop)
对齐的原则
结构体内存对齐与填充(Padding)是C语言中涉及数据结构存储方式的重要概念。它直接影响到程序的内存使用效率和性能,尤其在处理多平台或者低层次的系统编程时,需要对这一点有深入的理解。
1. 内存对齐的概念
内存对齐指的是将数据类型按一定的规则存储到内存中的方式。由于现代计算机处理器的存取效率,通常要求数据类型按一定的边界对齐存储。也就是说,不同类型的数据应该存放在特定的内存地址上,这样能够提高处理器访问内存的速度。
2. 内存对齐的原理
在C语言中,每个数据类型都有自己的“对齐要求”。对齐要求是指某个数据类型的变量在内存中应存储在某个特定的地址上,这个地址通常是该数据类型大小的倍数。
例如:
char
类型的数据通常可以存储在任意地址(1 字节对齐)。int
类型的变量通常要求存储在4字节对齐的地址上(即地址必须是4的倍数)。double
类型通常要求存储在8字节对齐的地址上(即地址必须是8的倍数)。
不同编译器可能会有不同的默认对齐方式,但是常见的规则是:
char
类型:1字节对齐。short
类型:2字节对齐。int
类型:4字节对齐。double
类型:8字节对齐。
3. 填充(Padding)
填充是指为了满足对齐要求,在结构体成员之间或结构体末尾插入空闲字节,以确保每个成员的数据按照其对齐要求存储。
举个例子,考虑一个结构体包含 char
和 int
类型的成员:
struct Example {char a; // 1 字节int b; // 4 字节
};
假设系统对 int
类型要求4字节对齐,那么在 char a
后面会有 3 个字节的填充,以确保 b
在 4 字节对齐的位置开始存储。这是因为 b
需要在内存中存储在地址是4的倍数的位置。
因此,结构体的内存布局可能如下:
| char a | padding | padding | padding | int b |
这个结构体的总大小将会是 8 字节,而不是 5 字节。这样,b
的起始地址就符合 4 字节对齐的要求。
4. 内存对齐与填充的规则
-
结构体成员对齐:
每个成员都必须存储在一个地址上,这个地址必须是该成员类型对齐要求的倍数。 -
结构体总对齐:
结构体的对齐要求是结构体中最大对齐要求成员的对齐要求。例如,如果结构体中有char
和double
成员,那么结构体的对齐要求就是double
的对齐要求,通常是 8 字节。 -
结构体大小:
结构体的大小是根据最大对齐要求来计算的。结构体的大小通常是结构体总内存的最小倍数,这个倍数是结构体内最大成员对齐的倍数。
5. 示例:结构体内存对齐与填充
让我们来看一个例子,假设在一个系统中,int
类型要求4字节对齐,char
类型要求1字节对齐:
#include <stdio.h>struct Example {char a; // 1 字节int b; // 4 字节char c; // 1 字节
};
这个结构体中的 a
需要 1 字节,而 b
需要 4 字节的对齐。由于 b
必须从 4 字节对齐的位置开始,因此 a
后面会有 3 个字节的填充,接着 b
存储。然后 c
占用 1 字节,由于 b
的对齐要求,结构体的总大小将根据最大对齐需求(通常为 4 字节)填充。
因此,结构体在内存中的布局如下:
| char a | padding | padding | padding | int b | char c | padding |
结构体总大小为 8 字节。可以通过 sizeof
操作符来查看结构体的实际大小:
printf("Size of struct Example: %zu\n", sizeof(struct Example)); // 输出:8
6. 编译器对齐设置
在一些情况下,编译器允许通过指令来设置对齐方式。例如,GCC 和 Clang 提供了 #pragma pack
指令,可以控制结构体的对齐方式。可以通过设置对齐大小来减小结构体的内存占用。
例如,在GCC中,使用 #pragma pack(1)
可以强制按 1 字节对齐,这样就不会有任何填充字节:
#pragma pack(1)
struct Example {char a; // 1 字节int b; // 4 字节
};
#pragma pack() // 恢复默认对齐
这样,结构体将没有填充字节,内存布局将是:
| char a | int b |
举例说明
2. 共用体的定义与使用
2.1 共用体的基本概念
共用体(union
)是一种特殊的数据结构,它与结构体类似,但与结构体不同的是,共用体的所有成员共享相同的内存空间。即同一时刻,共用体只能存储一个成员的值。这使得共用体能够节省内存空间。
共用体的定义格式如下:
union UnionName {type member1;type member2;type member3;// ...
};
2.2 共用体的定义与初始化
定义一个共用体并初始化时,通常初始化其中的第一个成员。
union Data {int i;float f;char c;
};union Data data1;
data1.i = 10;
data1.f = 3.14; // 此时 data1.i 的值会被覆盖
2.3 访问共用体成员
由于共用体的成员共享相同的内存位置,因此只能访问最后存储的成员。当一个成员被赋值时,其他成员的值将被覆盖。
union Data data1;
data1.i = 10;
printf("Integer: %d\n", data1.i);data1.f = 3.14;
printf("Float: %.2f\n", data1.f); // 访问 float 类型成员
2.4 共用体的应用场景
共用体主要用于节省内存,特别是在需要存储不同类型数据,但在任何时刻只需要其中之一的场合。常见的应用场景包括:
- 多种类型的数据共享同一内存空间。
- 处理不同类型数据的协议解析。
2.5 共用体与结构体的区别
- 内存分配:结构体中的每个成员都有自己独立的内存空间,而共用体的所有成员共享同一块内存空间。
- 用途:结构体适用于需要存储多个不同类型数据的场合,而共用体适用于需要存储不同类型数据,但在同一时刻只需要其中一个的场合。
3. 结构体与共用体与指针的结合
3.1 结构体指针
结构体指针是指向结构体类型变量的指针。通过结构体指针,可以访问结构体的成员。结构体指针通常与malloc
动态内存分配结合使用。
3.1.1 声明与初始化
struct Person {char name[50];int age;
};struct Person *ptr;
ptr = (struct Person *)malloc(sizeof(struct Person)); // 动态分配内存strcpy(ptr->name, "John");
ptr->age = 25;
3.1.2 访问结构体成员
通过结构体指针访问成员时,使用箭头操作符(->
)。
printf("Name: %s, Age: %d\n", ptr->name, ptr->age);
3.2 共用体指针
与结构体指针类似,我们也可以创建共用体指针,通过指针来访问共用体的成员。
3.2.1 声明与初始化
union Data {int i;float f;char c;
};union Data *ptr;
ptr = (union Data *)malloc(sizeof(union Data));ptr->i = 10;
3.2.2 访问共用体成员
与结构体指针类似,共用体指针也使用箭头操作符(->
)来访问成员。
printf("Integer: %d\n", ptr->i);
3.3 结构体与共用体混合使用
结构体和共用体也可以混合使用,以满足更复杂的需求。例如,我们可以在结构体中包含一个共用体,或者在共用体中使用结构体。
struct Person {char name[50];int age;
};union Data {struct Person p;int i;
};union Data data;
data.p.age = 30;
strcpy(data.p.name, "Alice");printf("Name: %s, Age: %d\n", data.p.name, data.p.age);
4.结论
结构体和共用体是C语言中非常强大的数据结构。结构体允许你将不同类型的数据组织在一起,而共用体通过共享内存来节省空间。在实际开发中,理解这两者的使用场景和优缺点,并掌握它们与指针的结合,是编写高效和内存优化代码的关键。
通过本篇文章的学习,希望你能够全面理解结构体与共用体的定义、使用方式及其在指针方面的应用,从而更好地应对C语言编程中的复杂问题。
相关文章:

【C语言】结构体与共用体深入解析
在C语言中,结构体(struct)和共用体(union)都是用来存储不同类型数据的复合数据类型,它们在程序设计中具有重要的作用。 推荐阅读:操作符详细解说,让你的编程技能更上一层楼 1. 结构体…...
es6.7.1分词器ik插件安装-和head插件连接es特殊配置
es6.7.1分词器ik插件安装-和head插件连接es特殊配置 如果对运维课程感兴趣,可以在b站上、A站或csdn上搜索我的账号: 运维实战课程,可以关注我,学习更多免费的运维实战技术视频 1.查看es6.7.1和es-head安装位置和es插件路径 [ro…...

java求职学习day18
常用的设计原则和设计模式 1 常用的设计原则(记住) 1.1 软件开发的流程 需求分析文档、概要设计文档、详细设计文档、编码和测试、安装和调试、维护和升级 1.2 常用的设计原则 (1)开闭原则(Open Close Principle…...
单链表专题(上)
链表的定义与创建 线性表: 1. 物理结构上不一定是线性的 2. 逻辑结构上一定是线性的 链表是一种物理存储结构上非连续,非顺序的存储结构 链表也是线性表的一种,但是在物理结构上不是连续的 链表是由一个一个的节点组成,需要数…...

【stm32学习】STM32F103相关特性
| 名称 | 缩写 | 频率 | 外部连接 | 功能 | 用途 | 特性 | |--------------------|------|----------------|---------------|------------|--------------|----------------| | 外部高速晶体振荡器 | HSE | 4~16MHz …...

PostGIS笔记:PostgreSQL中表、键和索引的基础操作
创建、查看与删除表 在数据库中创建一个表,使用如下代码: create table streets (id serial not null primary key, name varchar(50));这里的表名是streets,id是主键所以非空,采用serial数据类型,这个数据类型会自动…...

蓝桥杯python语言基础(3)——循环结构
一、for语句 理解range函数 range(start, stop, step) start: 序列开始的数字(默认为0)。stop: 序列结束的数字(不包含stop)。step: 步长(默认为1)。 练习 输出在 l 和 r 之间的所有偶数: pri…...

微服务网关鉴权之sa-token
目录 前言 项目描述 使用技术 项目结构 要点 实现 前期准备 依赖准备 统一依赖版本 模块依赖 配置文件准备 登录准备 网关配置token解析拦截器 网关集成sa-token 配置sa-token接口鉴权 配置satoken权限、角色获取 通用模块配置用户拦截器 api模块配置feign…...

23【进制的理解】
很多人可能听过计算机的最底层是2进制执行,但是原理并不知道,我们今天先不讨论那么复杂的问题,先讨论什么是进制 1910,10并不是1个字符,而是2个字符,也就是说在10进制里面没有“10”这个字符,1…...

jemalloc 5.3.0的tsd模块的源码分析
一、背景 在主流的内存库里,jemalloc作为android 5.0-android 10.0的默认分配器肯定占用了非常重要的一席之地。jemalloc的低版本和高版本之间的差异特别大,低版本的诸多网上整理的总结,无论是在概念上和还是在结构体命名上在新版本中很多都…...

【Convex Optimization Stanford】Lec3 Function
【Convex Optimization Stanford】Lec3 Function 前言凸函数的定义对凸函数在一条线上的限制增值扩充? 一阶条件二阶条件一些一阶/二阶条件的例子象集和sublevel set关于函数凸性的扩展(Jesen Inequality)保持函数凸性的操作非负加权和 & 仿射函数的…...

深入 Rollup:从入门到精通(三)Rollup CLI命令行实战
准备阶段:初始化项目 初始化项目,这里使用的是pnpm,也可以使用yarn或者npm # npm npm init -y # yarn yarn init -y # pnpm pnpm init安装rollup # npm npm install rollup -D # yarn yarn add rollup -D # pnpm pnpm install rollup -D在…...

wangEditor富文本编辑器,Laravel上传图片配置和使用
文章目录 前言步骤1. 构造好前端模版2. 搭建后端存储3. 调试 前言 由于最近写项目需要使用富文本编辑器,使用的是VUE3.0版本所以很多不兼容,实际测试以后推荐使用wangEditor 步骤 构造好前端模版搭建后端存储调试 1. 构造好前端模版 安装模版 模版安…...
chrome源码剖析—进程通信
Chrome 浏览器采用多进程架构(multi-process architecture),这种架构使得每个浏览器标签、扩展、插件、GPU 渲染等都在独立的进程中运行。为了确保不同进程之间的高效通信,Chrome 使用 进程间通信(IPC, Inter-Process …...
JJJ:linux时间子系统相关术语
文章目录 墙上时间内核管理的各种时间无时钟滴答模式(tickless mode 或 no-tick mode)简要介绍具体实现动态时钟滴答 Dynamic Ticks完全无时钟滴答(Full Tickless) nohz sleep单触发模式 oneshot mode 墙上时间 真实世界的真实时…...

0 基础学运维:解锁 K8s 云计算运维工程师成长密码
前言:作为一个过来人,我曾站在技术的门槛之外,连电脑运行内存和内存空间都傻傻分不清,完完全全的零基础。但如今,我已成长为一名资深的k8s云计算运维工程师。回顾这段历程,我深知踏上这条技术之路的艰辛与不…...
大一计算机的自学总结:位运算的应用及位图
前言 不仅异或运算有很多骚操作,位运算本身也有很多骚操作。(尤其后几个题,太逆天了) 一、2 的幂 class Solution { public:bool isPowerOfTwo(int n) {return n>0&&n(n&-n);} }; 根据二进制表示数的原理&#…...

计算机毕业设计Django+Tensorflow音乐推荐系统 机器学习 深度学习 音乐可视化 音乐爬虫 知识图谱 混合神经网络推荐算法 大数据毕设
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...

AI 图片涌入百度图库
在这个信息爆炸的时代,我们习惯了通过搜索引擎来获取各种想要的信息和图片。然而,现在打开搜索引擎看到的却是许多真假难辨的信息——AI图片,这部分数据正以惊人的速度涌入百度图库,让小编不禁想问:未来打开百度图库不…...

可爱狗狗的404动画页面HTML源码
源码介绍 可爱狗狗的404动画页面HTML源码,源码由HTMLCSSJS组成,记事本打开源码文件可以进行内容文字之类的修改,双击html文件可以本地运行效果 效果预览 源码获取 可爱狗狗的404动画页面HTML源码...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)
cd /home 进入home盘 安装虚拟环境: 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境: virtualenv myenv 3、激活虚拟环境(激活环境可以在当前环境下安装包) source myenv/bin/activate 此时,终端…...
面试高频问题
文章目录 🚀 消息队列核心技术揭秘:从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"?性能背后的秘密1.1 顺序写入与零拷贝:性能的双引擎1.2 分区并行:数据的"八车道高速公路"1.3 页缓存与批量处理…...
书籍“之“字形打印矩阵(8)0609
题目 给定一个矩阵matrix,按照"之"字形的方式打印这个矩阵,例如: 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为:1,…...

从零开始了解数据采集(二十八)——制造业数字孪生
近年来,我国的工业领域正经历一场前所未有的数字化变革,从“双碳目标”到工业互联网平台的推广,国家政策和市场需求共同推动了制造业的升级。在这场变革中,数字孪生技术成为备受关注的关键工具,它不仅让企业“看见”设…...
2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案
一、延迟敏感行业面临的DDoS攻击新挑战 2025年,金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征: AI驱动的自适应攻击:攻击流量模拟真实用户行为,差异率低至0.5%,传统规则引…...
FOPLP vs CoWoS
以下是 FOPLP(Fan-out panel-level packaging 扇出型面板级封装)与 CoWoS(Chip on Wafer on Substrate)两种先进封装技术的详细对比分析,涵盖技术原理、性能、成本、应用场景及市场趋势等维度: 一、技术原…...

SQL注入篇-sqlmap的配置和使用
在之前的皮卡丘靶场第五期SQL注入的内容中我们谈到了sqlmap,但是由于很多朋友看不了解命令行格式,所以是纯手动获取数据库信息的 接下来我们就用sqlmap来进行皮卡丘靶场的sql注入学习,链接:https://wwhc.lanzoue.com/ifJY32ybh6vc…...