news 2026/6/22 0:57:34

C语言结构体与共用体:一篇文章彻底搞懂

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C语言结构体与共用体:一篇文章彻底搞懂

🔥个人主页:代码不加冰(欢迎来访)
🎬作者简介:java后端学习者
❄️个人专栏:LeetCode刷题日记 , 苍穹外卖日记,SSM框架深入,JavaWeb,
命运的结局尽可永在,不屈的挑战却不可须臾或缺!

前言:

大家好,我是代码不加冰,今天还是主要复习C语言,后天要考试,感觉还是没底,因为在此之前一点都没学过,今天学到了结构体和共用体,在这里总结一下吧。

结构体(struct)和共用体(union)是C语言中最重要的构造类型。本文从内存布局到实际应用,带你一次掌握这两个核心知识点。


一、为什么需要结构体

1.1 基本数据类型的局限

C语言的基本数据类型(int、float、char等)只能表示单一数据。但现实世界中的实体往往是复合的——比如一个学生有学号、姓名、年龄、成绩等多个属性。

c // 不用结构体:散落的变量,难以管理 int stu_id = 1001; char stu_name[20] = "张三"; int stu_age = 20; float stu_score = 85.5; // 如果要表示100个学生?噩梦。

结构体就是用来把多个相关的数据打包成一个新的数据类型。


二、结构体(struct)

2.1 定义结构体类型

c // 语法格式 struct 结构体名 { 数据类型 成员1; 数据类型 成员2; // ... 更多成员 }; // 示例:定义学生类型 struct Student { int id; // 学号 char name[20]; // 姓名 int age; // 年龄 float score; // 成绩 }; // 注意:这里的分号不能省略!

关键理解struct Student只是定义了一个类型(蓝图),此时并没有分配内存。就像建筑师画了图纸,但还没盖房子。

2.2 声明结构体变量

c // 方式1:先定义类型,再声明变量 struct Student stu1; // 方式2:定义类型的同时声明变量 struct Student { int id; char name[20]; int age; float score; } stu1, stu2; // 直接声明了两个变量 // 方式3:省略结构体名(匿名结构体,不推荐) struct { int id; char name[20]; } stu1; // 无法再声明同类型的其他变量

2.3 初始化结构体变量

c // 方式1:按顺序初始化 struct Student stu1 = {1001, "张三", 20, 85.5}; // 方式2:指定成员初始化(C99,更安全) struct Student stu2 = { .id = 1002, .name = "李四", .score = 90.0, .age = 21 }; // 方式3:先声明后赋值 struct Student stu3; stu3.id = 1003; strcpy(stu3.name, "王五"); // 字符串不能直接赋值,要用strcpy stu3.age = 22; stu3.score = 88.0;

2.4 访问结构体成员

使用点运算符(.)

c #include <stdio.h> #include <string.h> int main() { struct Student stu = {1001, "张三", 20, 85.5}; // 访问和修改成员 printf("学号:%d\n", stu.id); printf("姓名:%s\n", stu.name); printf("年龄:%d\n", stu.age); printf("成绩:%.1f\n", stu.score); stu.score = 90.0; // 修改成绩 printf("修改后成绩:%.1f\n", stu.score); return 0; }

2.5 结构体数组

c struct Student class[30]; // 可以存储30个学生 // 初始化结构体数组 struct Student class[3] = { {1001, "张三", 20, 85.5}, {1002, "李四", 21, 90.0}, {1003, "王五", 19, 78.5} }; // 遍历访问 for (int i = 0; i < 3; i++) { printf("%s的成绩是%.1f\n", class[i].name, class[i].score); }

2.6 结构体指针

当结构体较大时,传递指针比传递整个结构体更高效。

c // 定义结构体指针 struct Student stu = {1001, "张三", 20, 85.5}; struct Student *p = &stu; // 通过指针访问成员:使用 -> 运算符 printf("姓名:%s\n", p->name); // 等价于 (*p).name printf("学号:%d\n", p->id); // 等价于 (*p).id // 修改成员 p->score = 95.0;

记忆口诀:结构变量用点(.),结构指针用箭头(->)。

2.7 结构体作为函数参数

c // 方式1:传值(复制整个结构体,效率低) void printStudent(struct Student s) { printf("姓名:%s,成绩:%.1f\n", s.name, s.score); } // 方式2:传指针(推荐,效率高,可修改原数据) void updateScore(struct Student *p, float new_score) { p->score = new_score; } // 使用 int main() { struct Student stu = {1001, "张三", 20, 85.5}; printStudent(stu); // 传值 updateScore(&stu, 95.0); // 传指针,直接修改原数据 return 0; }

2.8 结构体的内存对齐(重要考点)

c struct A { char c; // 1字节 int i; // 4字节 short s; // 2字节 }; // 大小不是 1+4+2=7,而是 12 字节! struct B { char c; // 1字节 short s; // 2字节 int i; // 4字节 }; // 大小是 8 字节

为什么?编译器为了CPU访问效率,会对结构体成员进行内存对齐

  • 每个成员的起始地址必须是其自身大小的整数倍

  • 结构体总大小必须是最大成员大小的整数倍

实际应用:如果对内存敏感,可以将成员按大小从大到小排列,减少填充浪费。


三、共用体(union)

3.1 什么是共用体

共用体的所有成员共享同一块内存空间。同一时刻只能存储一个成员的值。

c

union Data { int i; // 4字节 float f; // 4字节 char str[20]; // 20字节 };// 大小 = 20字节(取最大成员的大小)

3.2 定义和使用共用体

c #include <stdio.h> union Data { int i; float f; char str[20]; }; int main() { union Data data; data.i = 10; printf("data.i = %d\n", data.i); data.f = 3.14; // 覆盖了i的值 printf("data.f = %.2f\n", data.f); printf("data.i = %d\n", data.i); // 此时i的值已被破坏! // 同一时刻只能正确使用一个成员 return 0; }

3.3 结构体 vs 共用体

特性结构体(struct)共用体(union)
内存分配各成员独立分配,总大小≥各成员大小之和所有成员共享内存,总大小=最大成员大小
存储内容可同时存储所有成员的值同一时刻只能存储一个成员的值
用途表示具有多个属性的复合对象节省内存,同一数据的不同解释方式
安全性各成员互不影响一个成员改变会影响其他成员

3.4 共用体的典型应用

应用1:节省内存

c // 表示一个"值",可以是整数、浮点数或字符串 union Value { int int_val; float float_val; char *str_val; }; // 配合枚举使用,标记当前存储的是哪种类型 enum ValueType { INT, FLOAT, STRING }; struct Variable { enum ValueType type; union Value value; };

应用2:数据解析(同一块内存的不同解释)

c // 将4字节拆解为4个单独的字节 union IPAddress { unsigned int addr; // 32位IP地址 unsigned char bytes[4]; // 4个字节 }; int main() { union IPAddress ip; ip.addr = 0xC0A80101; // 192.168.1.1 // 通过字节数组访问每个字节 printf("%d.%d.%d.%d\n", ip.bytes[3], ip.bytes[2], ip.bytes[1], ip.bytes[0]); // 输出:192.168.1.1 return 0; }

四、结构体嵌套与复杂应用

4.1 结构体嵌套

c // 日期结构体 struct Date { int year; int month; int day; }; // 学生结构体包含日期 struct Student { int id; char name[20]; struct Date birthday; // 嵌套结构体 float score; }; int main() { struct Student stu = {1001, "张三", {2000, 5, 15}, 85.5}; // 访问嵌套成员 printf("出生日期:%d年%d月%d日\n", stu.birthday.year, stu.birthday.month, stu.birthday.day); return 0; }

4.2 结构体包含共用体

c // 表示不同类型的图形 struct Shape { char type; // 'C'表示圆,'R'表示矩形 union { struct { float radius; } circle; struct { float width, height; } rectangle; } data; }; int main() { struct Shape s; s.type = 'C'; s.data.circle.radius = 5.0; if (s.type == 'C') { printf("圆的面积:%.2f\n", 3.14 * s.data.circle.radius * s.data.circle.radius); } return 0; }

4.3 typedef简化结构体声明

c // 不用typedef struct Student { int id; char name[20]; }; struct Student stu1; // 每次都要写struct // 使用typedef typedef struct Student { int id; char name[20]; } Student; // 现在Student就是一个类型名了 Student stu1; // 不用写struct,更简洁 Student stu2; // 甚至可以这样(匿名结构体) typedef struct { int id; char name[20]; } Student; // 结构体本身没有名字,只有类型别名

五、综合实战:学生成绩管理系统

c #include <stdio.h> #include <string.h> #define MAX_STUDENTS 100 // 日期类型 typedef struct { int year; int month; int day; } Date; // 学生类型 typedef struct { int id; char name[20]; Date birthday; float scores[3]; // 三门课成绩 float avg; // 平均分 } Student; // 函数声明 void inputStudent(Student *s); void printStudent(Student s); float calcAverage(Student s); void sortByAvg(Student arr[], int n); int main() { Student students[MAX_STUDENTS]; int n = 0; int choice; while (1) { printf("\n===== 学生成绩管理系统 =====\n"); printf("1. 添加学生\n"); printf("2. 显示所有学生\n"); printf("3. 按平均分排序\n"); printf("4. 退出\n"); printf("请选择:"); scanf("%d", &choice); switch (choice) { case 1: if (n < MAX_STUDENTS) { inputStudent(&students[n]); students[n].avg = calcAverage(students[n]); n++; printf("添加成功!\n"); } else { printf("学生已满!\n"); } break; case 2: for (int i = 0; i < n; i++) { printStudent(students[i]); } break; case 3: sortByAvg(students, n); printf("排序完成!\n"); break; case 4: return 0; default: printf("无效选择!\n"); } } return 0; } void inputStudent(Student *s) { printf("请输入学号:"); scanf("%d", &s->id); printf("请输入姓名:"); scanf("%s", s->name); printf("请输入出生日期(年 月 日):"); scanf("%d %d %d", &s->birthday.year, &s->birthday.month, &s->birthday.day); printf("请输入三门课成绩:"); for (int i = 0; i < 3; i++) { scanf("%f", &s->scores[i]); } } void printStudent(Student s) { printf("学号:%d\t姓名:%s\t生日:%d-%d-%d\t成绩:%.1f %.1f %.1f\t平均:%.1f\n", s.id, s.name, s.birthday.year, s.birthday.month, s.birthday.day, s.scores[0], s.scores[1], s.scores[2], s.avg); } float calcAverage(Student s) { float sum = 0; for (int i = 0; i < 3; i++) { sum += s.scores[i]; } return sum / 3; } void sortByAvg(Student arr[], int n) { // 冒泡排序 for (int i = 0; i < n - 1; i++) { for (int j = 0; j < n - 1 - i; j++) { if (arr[j].avg < arr[j + 1].avg) { Student temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } }

六、期末高频考点速记

6.1 结构体相关

考点要点
结构体定义struct 名称 { 成员列表 };分号不能忘
变量声明struct 名称 变量名;或配合typedef简化
成员访问(变量)使用.运算符
成员访问(指针)使用->运算符
结构体大小受内存对齐影响,不等于各成员大小之和
结构体传参推荐传指针,避免大结构体复制开销

6.2 共用体相关

考点要点
定义union 名称 { 成员列表 };
内存所有成员共享同一块内存,大小=最大成员
限制同一时刻只能正确使用一个成员
用途节省内存、数据类型解析

6.3 常见陷阱

c // ❌ 陷阱1:字符串直接赋值 struct Student stu; stu.name = "张三"; // 错误!字符串数组不能直接赋值 // ✅ 正确做法 strcpy(stu.name, "张三"); // ❌ 陷阱2:忘记struct关键字(没用typedef时) Student stu; // 错误!C语言中要写struct Student // ✅ 正确做法 struct Student stu; // ❌ 陷阱3:共用体同时使用多个成员 union Data d; d.i = 10; printf("%f", d.f); // 错误!刚存了整数却当浮点数读 // ❌ 陷阱4:结构体比较 struct Student s1, s2; if (s1 == s2) { } // 错误!不能直接比较结构体 // 需要逐个成员比较

七、总结对比表

对比项结构体(struct)共用体(union)
关键字structunion
内存模型各成员独立存储所有成员共享内存
总大小≥各成员大小之和(有对齐填充)=最大成员大小
成员关系可同时使用所有成员同一时刻只能用其中一个
典型用途表示复合对象(学生、商品等)节省内存、多类型数据存储
修改影响只修改被操作的成员修改一个会影响其他所有成员

一句话总结:结构体是各占各的地,大家互不干扰;共用体是轮流住一间房,一次只能住一个人。理解这个本质区别,就能轻松应对考试和实际编程。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/22 0:57:04

Selenium自动化测试入门:彻底解决ChromeDriver配置与版本匹配难题

1. 项目概述&#xff1a;为什么Selenium新手总在Driver上栽跟头&#xff1f;如果你刚开始接触Selenium自动化测试或爬虫&#xff0c;大概率已经听过或者亲身经历过这个经典错误&#xff1a;WebDriverException: Message: ‘chromedriver’ executable needs to be in PATH。这个…

作者头像 李华
网站建设 2026/6/22 0:52:01

深度强化学习驱动AM-RIS与流体天线优化全双工网络能效

1. 项目概述与核心价值 最近在折腾一个挺有意思的项目&#xff0c;核心是解决下一代无线通信网络里一个老大难问题&#xff1a;能耗。大家可能都听说过5G甚至6G基站功耗有多吓人&#xff0c;运营商每年的电费账单里&#xff0c;基站能耗占了很大一块。我们这次搞的&#xff0c;…

作者头像 李华
网站建设 2026/6/22 0:41:40

ModTheSpire终极指南:如何在5分钟内为《杀戮尖塔》安装无限模组

ModTheSpire终极指南&#xff1a;如何在5分钟内为《杀戮尖塔》安装无限模组 【免费下载链接】ModTheSpire External mod loader for Slay The Spire 项目地址: https://gitcode.com/gh_mirrors/mo/ModTheSpire ModTheSpire是《杀戮尖塔》的官方外部模组加载器&#xff0…

作者头像 李华
网站建设 2026/6/22 0:33:24

LangChain本地RAG速成:七天从零搭建FAISS知识库

1. 项目概述&#xff1a;为什么“一周速成”不是画饼&#xff0c;而是可落地的本地化学习路径 LangChain 这个词最近半年在技术社区里出现的频率&#xff0c;已经不亚于当年 Docker 刚火起来时的状态。但和 Docker 不同的是&#xff0c;LangChain 的学习曲线更陡峭——它不像一…

作者头像 李华
网站建设 2026/6/22 0:23:13

OpenCore Auxiliary Tools:黑苹果配置架构革命与全栈技术解码

OpenCore Auxiliary Tools&#xff1a;黑苹果配置架构革命与全栈技术解码 【免费下载链接】OCAuxiliaryTools Cross-platform GUI management tools for OpenCore&#xff08;OCAT&#xff09; 项目地址: https://gitcode.com/gh_mirrors/oc/OCAuxiliaryTools 在当今黑苹…

作者头像 李华