当前位置: 首页 > web >正文

C语言结构体与联合体详解

目录

    • 结构体(Structure)
      • 基本概念
      • 结构体定义与声明
      • 结构体嵌套
      • 结构体指针
      • 结构体数组
      • 结构体的内存布局
      • 结构体作为函数参数
    • 联合体(Union)
      • 基本概念
      • 联合体定义与声明
      • 联合体成员访问
      • 联合体的内存布局
      • 联合体嵌套
      • 联合体的应用场景
    • 结构体与联合体的对比
    • 实际应用示例
      • 结构体应用示例:学生信息管理
      • 联合体应用示例:变体数据类型
    • 注意事项
      • 结构体注意事项
      • 联合体注意事项
    • 总结

结构体(Structure)和联合体(Union)是C语言中两种重要的自定义数据类型,它们允许程序员将不同类型的数据组合在一起,形成更复杂的数据结构。虽然它们在语法上相似,但在内存布局和使用场景上有本质区别。

结构体(Structure)

基本概念

结构体是一种用户自定义的数据类型,用于将多个不同类型的数据项组合在一起。每个数据项称为结构体的成员(Member),它们在内存中依次排列。

结构体定义与声明

// 结构体定义
struct Person {char name[50];    // 姓名int age;          // 年龄float height;     // 身高
};  // 注意分号不能省略// 声明结构体变量
struct Person p1;  // 声明一个Person类型的变量// 定义并初始化
struct Person p2 = {"张三",25,175.5
};// 访问结构体成员
printf("姓名: %s\n", p2.name);
printf("年龄: %d\n", p2.age);

结构体嵌套

结构体可以包含其他结构体作为成员:

// 日期结构体
struct Date {int year;int month;int day;
};// 包含Date结构体的Person结构体
struct Person {char name[50];int age;struct Date birthday;  // 嵌套结构体
};// 初始化嵌套结构体
struct Person p = {"李四",30,{1990, 5, 15}  // 初始化birthday成员
};// 访问嵌套结构体成员
printf("出生日期: %d-%d-%d\n", p.birthday.year, p.birthday.month, p.birthday.day);

结构体指针

通过结构体指针可以间接访问结构体成员:

struct Person {char name[50];int age;
};int main() {struct Person p = {"王五", 28};struct Person *ptr = &p;  // 指向结构体的指针// 通过指针访问成员的两种方式printf("姓名: %s\n", (*ptr).name);  // 使用解引用操作符printf("年龄: %d\n", ptr->age);     // 使用箭头操作符return 0;
}

结构体数组

结构体数组是一组具有相同结构体类型的变量:

struct Point {int x;int y;
};int main() {// 结构体数组定义与初始化struct Point points[3] = {{1, 2},{3, 4},{5, 6}};// 访问结构体数组元素for (int i = 0; i < 3; i++) {printf("点 %d: (%d, %d)\n", i+1, points[i].x, points[i].y);}return 0;
}

结构体的内存布局

结构体的成员在内存中依次排列,但可能存在内存对齐(Alignment):

struct Example {char c;     // 1字节int i;      // 4字节char d;     // 1字节
};// 在32位系统上,sizeof(struct Example)通常为12字节,而非6字节
// 原因是内存对齐:char后填充3字节,使int从4字节边界开始

结构体作为函数参数

结构体可以作为函数参数传递,有两种方式:

struct Rectangle {int width;int height;
};// 1. 值传递(复制整个结构体)
int areaByValue(struct Rectangle r) {return r.width * r.height;
}// 2. 指针传递(传递结构体地址)
int areaByPointer(struct Rectangle *r) {return r->width * r->height;
}int main() {struct Rectangle rect = {10, 5};printf("面积(值传递): %d\n", areaByValue(rect));printf("面积(指针传递): %d\n", areaByPointer(&rect));return 0;
}

联合体(Union)

基本概念

联合体是一种特殊的数据类型,允许在相同的内存位置存储不同类型的数据。联合体的所有成员共享同一块内存空间,因此联合体的大小等于其最大成员的大小。

联合体定义与声明

// 联合体定义
union Data {int i;          // 4字节float f;        // 4字节char str[20];   // 20字节
};  // 注意分号不能省略// 声明联合体变量
union Data data;  // 声明一个Data类型的变量// 联合体大小
printf("联合体大小: %lu 字节\n", sizeof(data));  // 输出20(最大成员大小)

联合体成员访问

联合体在同一时间只能存储一个成员的值:

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 = 22.5;printf("data.f: %f\n", data.f);// 存储字符串(覆盖之前的浮点数)strcpy(data.str, "Hello");printf("data.str: %s\n", data.str);return 0;
}

联合体的内存布局

联合体的所有成员共享同一块内存空间:

union Mixed {int i;         // 4字节char c[4];     // 4字节
};int main() {union Mixed m;m.i = 0x12345678;// 在小端序系统上,内存布局为:// m.c[0]: 0x78// m.c[1]: 0x56// m.c[2]: 0x34// m.c[3]: 0x12printf("m.c[0]: 0x%x\n", m.c[0]);  // 输出0x78(小端序)return 0;
}

联合体嵌套

联合体可以嵌套在结构体中,反之亦然:

// 嵌套联合体的结构体
struct Record {int id;char type;  // 'I'表示整数,'F'表示浮点数union {int iValue;float fValue;} value;  // 匿名联合体
};int main() {struct Record r1 = {1, 'I', {.iValue = 100}};struct Record r2 = {2, 'F', {.fValue = 3.14f}};if (r1.type == 'I') {printf("r1的整数值: %d\n", r1.value.iValue);}if (r2.type == 'F') {printf("r2的浮点值: %f\n", r2.value.fValue);}return 0;
}

联合体的应用场景

  1. 节省内存:当一个变量可能有多种类型,但同一时间只使用一种类型时
  2. 类型转换:在不同数据类型之间进行位模式转换
  3. 访问数据的不同表示:例如访问整数的各个字节
  4. 变体数据结构:实现可以存储不同类型数据的通用容器

结构体与联合体的对比

特性结构体(Structure)联合体(Union)
内存布局成员依次排列,可能有内存对齐所有成员共享同一块内存空间
大小计算所有成员大小之和(考虑对齐)最大成员的大小
成员访问所有成员可同时访问同一时间只能访问一个成员
数据存储每个成员存储独立的数据所有成员共享相同的数据存储空间
内存占用通常较大通常较小
适用场景组合不同类型的数据节省内存,处理变体数据

实际应用示例

结构体应用示例:学生信息管理

#include <stdio.h>
#include <string.h>// 学生结构体定义
struct Student {char name[50];int id;float scores[3];  // 三门课程的成绩
};// 计算平均分
float calculateAverage(struct Student *s) {float sum = 0;for (int i = 0; i < 3; i++) {sum += s->scores[i];}return sum / 3;
}int main() {// 初始化学生数组struct Student students[2] = {{"张三", 101, {85.5, 90.0, 78.5}},{"李四", 102, {92.0, 87.5, 95.0}}};// 显示学生信息for (int i = 0; i < 2; i++) {printf("学生: %s (ID: %d)\n", students[i].name, students[i].id);printf("成绩: %.1f, %.1f, %.1f\n", students[i].scores[0], students[i].scores[1], students[i].scores[2]);printf("平均分: %.2f\n\n", calculateAverage(&students[i]));}return 0;
}

联合体应用示例:变体数据类型

#include <stdio.h>// 变体数据类型
union Variant {int i;float f;char str[20];
};// 数据类型枚举
enum Type {INT, FLOAT, STRING};// 数据结构
struct Data {enum Type type;union Variant value;
};// 打印变体数据
void printData(struct Data d) {switch (d.type) {case INT:printf("整数值: %d\n", d.value.i);break;case FLOAT:printf("浮点值: %f\n", d.value.f);break;case STRING:printf("字符串值: %s\n", d.value.str);break;}
}int main() {// 创建不同类型的数据struct Data d1 = {INT, {.i = 42}};struct Data d2 = {FLOAT, {.f = 3.14f}};struct Data d3 = {STRING, {.str = "Hello"}};// 打印数据printData(d1);printData(d2);printData(d3);return 0;
}

注意事项

结构体注意事项

  1. 内存对齐:结构体大小可能大于成员大小之和,编译器会插入填充字节以满足对齐要求
  2. 结构体复制:结构体赋值会复制整个结构体,对于大型结构体可能影响性能
  3. 结构体指针传递:对于大型结构体,使用指针传递可提高性能
  4. 结构体嵌套:避免过度嵌套导致代码复杂度过高

联合体注意事项

  1. 成员覆盖:写入一个成员会覆盖其他成员的值
  2. 类型安全:必须跟踪当前存储的成员类型,否则访问错误类型会导致未定义行为
  3. 对齐要求:联合体的对齐方式由其最大成员决定
  4. 应用限制:联合体主要用于节省内存,不适合需要同时存储多个值的场景

总结

结构体和联合体是C语言中两种重要的自定义数据类型,它们各自有独特的内存布局和应用场景:

  • 结构体用于组合不同类型的数据,每个成员有独立的内存空间
  • 联合体用于节省内存,所有成员共享同一块内存空间
  • 结构体适合需要同时存储多个相关数据的场景
  • 联合体适合处理变体数据或需要在不同类型间共享位模式的场景

理解结构体和联合体的区别和联系,能够帮助程序员设计更高效、更灵活的数据结构,满足不同的编程需求。

http://www.xdnf.cn/news/14132.html

相关文章:

  • Windows批处理脚本(.bat脚本、.bat语法)关闭回显@echo off、延迟变量扩展setlocal enabledelayedexpansion
  • 【教程】Windows安全中心扫描设置排除文件
  • Ubuntu Server 24.04|22.04|20.04|18.04 安装GUI DESKTOP xfce4
  • 文本表示的发展概述
  • SpringAI使用总结
  • [蓝桥杯 2023 国 B] AB 路线 (BFS)
  • 事务传播行为详解
  • 学习日记-day29-6.13
  • SpringBoot+vue前后端分离系统开发(期末)
  • 让高端装备“先跑起来”:虚拟仿真验证平台重塑研制流程
  • HarmonyOS5 运动健康app(二):健康跑步(附代码)
  • 多文件,多开发环境配置 Spring boot
  • 【QT】字符串类应用与常用基本数据类型
  • 驭码CodeRider 2.0深度体验:全流程智能体如何重塑研发协作范式?
  • 双向链表——(有头双向循环链表)
  • 轻量级密码算法Grain-128a的Python实现
  • Java求职者面试指南:Spring, Spring Boot, Spring MVC, MyBatis技术深度解析
  • 电商运营公司排名
  • 08 - CoTAttention模块
  • 使用Claude Desktop快速体验MCP servers!
  • 短剧热浪,席卷海内外。
  • Rust编写Shop管理系统
  • 长春光博会 | 麒麟信安:构建工业数字化安全基座,赋能智能制造转型升级
  • 深入剖析Redis高性能的原因,IO多路复用模型,Redis数据迁移,分布式锁实现
  • Python数据可视化:Seaborn入门与实践
  • LeetCode 744.寻找比目标字母大的最小字母
  • 【动手学深度学习】3.5. 图像分类数据集
  • 3D模型格式转换HOOPS Exchange与工程设计软件自带转换器对比分析
  • 力扣-322.零钱兑换
  • 最新四六级写作好词好句锦囊(持续更新中)