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

【C语言】自定义类型:联合体与枚举

文章目录

    • 一、联合体(Union):内存共用的灵活类型
      • 1.1 什么是联合体?
      • 1.2 联合体的核心特点
      • 1.3 联合体与结构体的对比
      • 1.4 联合体大小的计算规则
      • 1.5 联合体的典型应用场景
        • 场景1:节省内存(互斥属性存储)
        • 场景2:判断机器字节序
    • 二、枚举(Enum):有限取值的清晰表示
      • 2.1 什么是枚举?
      • 2.2 枚举的优势(对比#define)
      • 2.3 枚举的使用方法
    • 三、总结

在C语言中,自定义类型为数据组织提供了灵活的方式。除了常见的结构体,联合体(共用体)和枚举也是非常实用的类型。它们在内存管理和代码可读性方面有着独特的优势,本文将详细解析这两种类型的特性、用法及应用场景。

一、联合体(Union):内存共用的灵活类型

1.1 什么是联合体?

联合体是一种特殊的自定义类型,它由多个不同类型的成员组成,但所有成员共用同一块内存空间。这意味着联合体的大小仅需满足最大成员的存储需求,相比结构体能显著节省内存。

声明方式与结构体类似,关键字为union

// 联合体类型声明
union Un {char c;  // 字符型成员int i;   // 整型成员
};// 联合体变量定义
union Un un;

1.2 联合体的核心特点

  1. 内存共用:所有成员共享同一块内存,起始地址相同。

    #include <stdio.h>
    union Un {char c;int i;
    };
    int main() {union Un un;printf("&un = %p\n", &un);    // 输出:001AF85Cprintf("&un.i = %p\n", &un.i);// 输出:001AF85Cprintf("&un.c = %p\n", &un.c);// 输出:001AF85Creturn 0;
    }
    

    上述代码中,联合体变量un及其成员ic的地址完全相同,证明它们共用内存。

  2. 成员值相互影响:修改一个成员的值会影响其他成员。

    #include <stdio.h>
    union Un {char c;int i;
    };
    int main() {union Un un;un.i = 0x11223344;  // 赋值整型成员un.c = 0x55;        // 修改字符型成员printf("%x\n", un.i);  // 输出:11223355return 0;
    }
    

    原理:int类型占4字节,char占1字节。un.c修改的是un.i的第一个字节(低地址位),导致un.i的原值0x11223344变为0x11223355

1.3 联合体与结构体的对比

类型内存布局特点大小计算方式
结构体(struct)成员占用独立内存,按对齐规则排列所有成员大小之和(含填充字节)
联合体(union)成员共用一块内存至少为最大成员大小(需满足对齐)

示例对比

// 结构体:成员独立占用内存
struct S {char c;  // 1字节int i;   // 4字节(因对齐,c后填充3字节)
};
// 大小:1 + 3(填充) + 4 = 8字节// 联合体:成员共用内存
union Un {char c;  // 1字节int i;   // 4字节
};
// 大小:4字节(取最大成员大小)

1.4 联合体大小的计算规则

  1. 基础规则:联合体大小至少为最大成员的大小。
  2. 对齐规则:若最大成员大小不是“最大对齐数”的整数倍,需向上对齐到整数倍。
    (对齐数:成员自身大小与默认对齐数的较小值,默认对齐数通常为4或8)

示例计算

#include <stdio.h>// 案例1:char[5](大小5)与int(大小4)
union Un1 {char c[5];  // 对齐数1,大小5int i;      // 对齐数4,大小4
};
// 最大成员大小5,最大对齐数4。5不是4的倍数,向上对齐到8(4×2)。
// sizeof(Un1) = 8// 案例2:short[7](大小14)与int(大小4)
union Un2 {short c[7];  // 对齐数2,大小14int i;       // 对齐数4,大小4
};
// 最大成员大小14,最大对齐数4。14不是4的倍数,向上对齐到16(4×4)。
// sizeof(Un2) = 16int main() {printf("%d\n", sizeof(union Un1));  // 输出:8printf("%d\n", sizeof(union Un2));  // 输出:16return 0;
}

1.5 联合体的典型应用场景

场景1:节省内存(互斥属性存储)

当数据包含“公共属性”和“互斥的特殊属性”时,用联合体存储特殊属性可减少内存浪费。
例如“礼品兑换单”设计:

// 优化前:结构体包含所有属性,浪费内存
struct gift_list {// 公共属性int stock_number;  // 库存量double price;      // 价格int item_type;     // 商品类型(1-图书,2-杯子,3-衬衫)// 特殊属性(互斥,仅一种生效)char title[20];    // 书名(仅图书)char author[20];   // 作者(仅图书)int num_pages;     // 页数(仅图书)char design[30];   // 设计(仅杯子/衬衫)int colors;        // 颜色(仅衬衫)int sizes;         // 尺寸(仅衬衫)
};// 优化后:用联合体存储特殊属性
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;
};

优化后,特殊属性仅占用最大成员的内存(如shirt结构体大小),避免了无效属性的内存浪费。

场景2:判断机器字节序

字节序是数据在内存中的存储顺序(大端/小端),联合体可简单判断:

// 返回1:小端(低地址存低位数据);返回0:大端(低地址存高位数据)
int check_sys() {union {int i;    // 4字节:0x00000001char c;   // 1字节:取i的低地址位} un;un.i = 1;return un.c;  // 小端:c=1;大端:c=0
}

二、枚举(Enum):有限取值的清晰表示

2.1 什么是枚举?

枚举用于定义有限个可能取值的集合,例如星期、性别、颜色等。它将离散值命名,增强代码可读性。

声明方式

// 枚举类型声明(默认值从0开始递增)
enum Day {Mon,   // 0Tues,  // 1Wed,   // 2Thur,  // 3Fri,   // 4Sat,   // 5Sun    // 6
};// 手动指定枚举值
enum Color {RED = 2,   // 2GREEN = 4, // 4BLUE = 8   // 8
};

enum Dayenum Color为枚举类型,MonRED等为枚举常量

2.2 枚举的优势(对比#define)

优势说明
增强可读性RED代替1,代码意图更清晰。
类型检查更严格枚举变量只能接收枚举常量,#define定义的常量无类型限制。
便于调试预处理阶段会删除#define符号,而枚举常量在调试时可见。
批量定义常量一次声明多个相关常量,无需重复写#define
遵循作用域规则枚举声明在函数内时,仅在函数内有效,避免全局命名污染。

2.3 枚举的使用方法

  1. 定义枚举变量

    enum Color { RED, GREEN, BLUE };
    enum Color clr = GREEN;  // 用枚举常量赋值
    
  2. 赋值规则

    • C语言允许直接赋值整数(不推荐,破坏类型检查):
      enum Color clr = 2;  // C中允许,C++中禁止
      
    • 建议始终使用枚举常量赋值,保证代码规范性。

三、总结

  • 联合体:通过成员共用内存实现内存优化,适合存储互斥属性,大小计算需考虑最大成员和对齐规则。
  • 枚举:清晰表示有限取值集合,相比#define更安全、易维护,适合定义状态码、选项等。

合理使用这两种类型,能让代码更高效、更易读,尤其在嵌入式开发(内存受限)和大型项目(代码规范性要求高)中尤为重要。

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

相关文章:

  • SpringMVC在前后端分离架构中的执行流程详解
  • 句子表征-文本匹配--representation-based/interactive-based
  • MS-DOS 常用指令集
  • 机器学习——学习路线
  • 2.Java和C++有什么区别
  • Demo-LangGraph构建Agent
  • 【Spring】SpringBoot 自动配置,@ComponentScan、@Import、ImportSelector接口
  • LeetCode 132:分割回文串 II
  • Linux开发利器:探秘开源,构建高效——基础开发工具指南(下)【make/Makefile】
  • 水面垃圾清扫船cad【6张】三维图+设计说明书
  • Jmeter进行性能并发测试
  • 【Java】使用FreeMarker来实现Word自定义导出
  • C++高频知识点(十四)
  • 京东商品详情API技术文档框架及Python实现方案
  • sqli-labs:Less-27a关卡详细解析
  • 《Python 实用项目与工具制作指南》· 2.3 导入
  • Bean的生命周期和循环依赖问题的解决
  • curl发送文件bodyParser无法获取请求体的问题分析
  • 嵌入式硬件中三极管推挽电路控制与实现
  • PPT自动化 python-pptx - 11 : 备注页 (Notes Slides)
  • (论文速读)Text-IF:基于语义文本引导的退化感知交互式图像融合方法
  • sqli-labs-master/Less-31~Less-40
  • openeuler离线安装软件
  • Hexo - 免费搭建个人博客07 - 添加右上角的“目录”
  • 先知模型或者说从容的模型
  • Linux—yum仓库及NFS网络共享服务
  • Java基础-斗地主游戏
  • opencv引入libavif
  • 从 0 到 1 开发图书管理系统:飞算 JavaAI 让技术落地更简单
  • Prometheus-3--Prometheus是怎么抓取Java应用,Redis中间件,服务器环境的指标的?