c语言 union struct

99ANYc3cd6
预计阅读时长 19 分钟
位置: 首页 C语言 正文

Part 1: struct (结构体)

1 是什么?

结构体是一种“打包”的数据类型,它允许你将多个不同类型的变量组合在一起形成一个单一的、复合的变量,你可以把它想象成一个“记录”或一个“对象”的简单版本,它包含多个字段。

c语言 union struct
(图片来源网络,侵删)

2 为什么用?

当你需要描述一个具有多个属性的事物时,结构体非常适用,一个学生有学号、姓名、年龄、成绩等多个属性,这些属性类型不同,但都属于“学生”这个概念。

3 如何定义和使用?

定义结构体类型:

struct Student {
    int id;         // 学号
    char name[50];  // 姓名
    int age;        // 年龄
    float score;    // 成绩
};

这里我们定义了一个名为 struct Student 的数据类型,但还没有创建任何变量。

声明结构体变量:

c语言 union struct
(图片来源网络,侵删)
// 方式一:在定义结构体时直接声明变量
struct Student {
    int id;
    char name[50];
} student1, student2;
// 方式二:使用已定义的结构体类型声明变量
struct Student student3, student4;

访问结构体成员: 使用成员访问运算符 (点) 来访问和修改结构体内部的变量。

#include <stdio.h>
#include <string.h>
// 定义结构体
struct Student {
    int id;
    char name[50];
    int age;
};
int main() {
    // 声明并初始化一个结构体变量
    struct Student student1 = {101, "张三", 20};
    // 访问并打印成员
    printf("学号: %d\n", student1.id);
    printf("姓名: %s\n", student1.name);
    printf("年龄: %d\n", student1.age);
    // 修改成员的值
    student1.age = 21;
    printf("修改后的年龄: %d\n", student1.age);
    return 0;
}

4 内存布局

结构体的内存大小是其所有成员大小的总和,并可能因为内存对齐(Memory Alignment)而存在“空洞”或填充字节,以提升 CPU 访问效率。

struct Example {
    char c;    // 1 字节
    int i;     // 4 字节
    short s;   // 2 字节
};
// 假设对齐方式为 4 字节
// 内存布局: [c][ ][ ][i][i][i][i][s][s][ ]
// 总大小通常是 12 字节,而不是 1+4+2=7 字节。

Part 2: union (联合体)

1 是什么?

联合体是一种“共享”的数据类型,它允许你在同一个内存地址存储不同类型的数据,其核心特点是:所有成员共享同一块内存空间

2 为什么用?

联合体的主要目的是节省内存,当你知道一个数据块在同一时间只会以一种形式存在时,就可以使用联合体,一个变量可能需要存储一个整数,或者一个字符,或者一个浮点数,但绝不会同时存储它们。

c语言 union struct
(图片来源网络,侵删)

3 如何定义和使用?

定义联合体类型:

union Data {
    int i;
    float f;
    char str[20];
};

声明联合体变量:

// 方式一:在定义时声明
union Data {
    int i;
    float f;
} data1;
// 方式二:使用已定义的类型声明
union Data data2;

访问联合体成员: 同样使用 运算符。

关键点: 由于所有成员共享内存,你对任何一个成员的写入都会覆盖其他成员的值,读取时,你只能读取最后一次写入的那个类型的数据。

#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);
    // 再以浮点数形式写入,这会覆盖 i 的值
    data.f = 220.5;
    printf("data.f: %f\n", data.f);
    // 此时再打印 data.i,其值已经被破坏,是未定义的或毫无意义的
    printf("data.i (after f assigned): %d (This is garbage!)\n", data.i);
    // 最后以字符串形式写入,这会覆盖 f 的值
    strcpy(data.str, "Hello");
    printf("data.str: %s\n", data.str);
    // 此时再打印 data.f,其值也已经被破坏
    printf("data.f (after str assigned): %f (This is also garbage!)\n", data.f);
    return 0;
}

4 内存布局

联合体的内存大小是其所有成员中占用空间最大的那个成员的大小,因为所有成员都从同一个内存地址开始存放。

union Example {
    char c;    // 1 字节
    int i;     // 4 字节
    short s;   // 2 字节
};
// 这个联合体的大小是 4 字节,因为 int i 是最大的成员。
// 内存布局: [ ][ ][ ][c/i/s] (所有成员都从第一个字节开始)

Part 3: struct vs union 核心对比

特性 struct (结构体) union (联合体)
核心思想 打包 共享
内存占用 所有成员大小的总和 (考虑对齐) 最大成员的大小
成员关系 每个成员有自己独立的内存空间 所有成员共享同一块内存空间
数据存储 可以同时存储所有成员的数据 同一时间只能有效存储一个成员的数据
典型用途 描述一个事物的多个属性,如学生、员工信息 节省内存,当数据有多种类型但不同时存在时,如数据解析、硬件寄存器访问

Part 4: 实际应用场景

结构体 的应用

几乎无处不在,是组织复杂数据的基础。

  • 学生信息管理系统
  • 图形学中的点坐标 struct Point { int x; int y; };
  • 网络编程中的地址 struct sockaddr_in { ... };
  • 操作系统中的进程控制块 struct task_struct { ... };

联合体 的应用

场景相对特定,但非常高效。

  • 网络数据解析:一个网络数据包的载荷部分可能是整数、浮点数或字符串,你可以用联合体来解析它。

    union PacketPayload {
        int int_value;
        float float_value;
        char char_array[128];
    };
  • 硬件寄存器访问:一个硬件寄存器可能被读写为字节、字或双字,用联合体可以方便地访问。

    // 假设 0x1234 是一个硬件寄存器的地址
    volatile union {
        uint8_t byte;
        uint16_t word;
        uint32_t dword;
    } *hw_reg = (union volatile ... *)0x1234;
    hw_reg->byte = 0xAA;   // 以字节方式写入
    uint16_t value = hw_reg->word; // 以字方式读取
  • 实现“变体”类型:比如一个可以存储多种类型值的变量(类似于 C++ 的 std::variant)。

一个经典的 union + struct 组合:tagged union (带标签的联合体)

这是联合体最强大的用法之一,用于安全地处理多种可能类型的数据,你需要一个额外的 struct 成员(标签)来记录当前联合体中存储的是哪种类型。

#include <stdio.h>
#include <string.h>
// 定义类型标签
typedef enum { TYPE_INT, TYPE_FLOAT, TYPE_STRING } DataType;
// 定义带标签的联合体
typedef struct {
    DataType type; // 标签,指示当前有效的是哪个成员
    union {
        int i_val;
        float f_val;
        char* s_val;
    } data;
} Variant;
void printVariant(Variant v) {
    switch (v.type) {
        case TYPE_INT:
            printf("Integer: %d\n", v.data.i_val);
            break;
        case TYPE_FLOAT:
            printf("Float: %f\n", v.data.f_val);
            break;
        case TYPE_STRING:
            printf("String: %s\n", v.data.s_val);
            break;
        default:
            printf("Unknown type\n");
    }
}
int main() {
    Variant v1, v2, v3;
    v1.type = TYPE_INT;
    v1.data.i_val = 123;
    v2.type = TYPE_FLOAT;
    v2.data.f_val = 456.789f;
    v3.type = TYPE_STRING;
    v3.data.s_val = "Hello, Union!";
    printVariant(v1);
    printVariant(v2);
    printVariant(v3);
    return 0;
}

这个模式非常强大,是许多动态类型语言和高级数据结构实现的基础。

  • struct 当你想把几个东西“放在一起”,形成一个整体,它们会各自占据自己的空间。
  • union 当你想让几个东西“共用一个地方”,节省空间,但要知道同一时间只能用其中一个。

理解它们的区别是掌握 C 语言内存管理和数据组织的关键一步。

-- 展开阅读全文 --
头像
objective c 动态语言
« 上一篇 2025-12-23
dede list diy数据表
下一篇 » 2025-12-23

相关文章

取消
微信二维码
支付宝二维码

目录[+]