【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源码...
(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...
工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
渗透实战PortSwigger靶场-XSS Lab 14:大多数标签和属性被阻止
<script>标签被拦截 我们需要把全部可用的 tag 和 event 进行暴力破解 XSS cheat sheet: https://portswigger.net/web-security/cross-site-scripting/cheat-sheet 通过爆破发现body可以用 再把全部 events 放进去爆破 这些 event 全部可用 <body onres…...
postgresql|数据库|只读用户的创建和删除(备忘)
CREATE USER read_only WITH PASSWORD 密码 -- 连接到xxx数据库 \c xxx -- 授予对xxx数据库的只读权限 GRANT CONNECT ON DATABASE xxx TO read_only; GRANT USAGE ON SCHEMA public TO read_only; GRANT SELECT ON ALL TABLES IN SCHEMA public TO read_only; GRANT EXECUTE O…...
[免费]微信小程序问卷调查系统(SpringBoot后端+Vue管理端)【论文+源码+SQL脚本】
大家好,我是java1234_小锋老师,看到一个不错的微信小程序问卷调查系统(SpringBoot后端Vue管理端)【论文源码SQL脚本】,分享下哈。 项目视频演示 【免费】微信小程序问卷调查系统(SpringBoot后端Vue管理端) Java毕业设计_哔哩哔哩_bilibili 项…...
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
