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

目录

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

结构体(Structure)

基本概念

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

结构体定义与声明

c 复制代码
// 结构体定义
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);

结构体嵌套

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

c 复制代码
// 日期结构体
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);

结构体指针

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

c 复制代码
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;
}

结构体数组

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

c 复制代码
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):

c 复制代码
struct Example {
    char c;     // 1字节
    int i;      // 4字节
    char d;     // 1字节
};

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

结构体作为函数参数

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

c 复制代码
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)

基本概念

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

联合体定义与声明

c 复制代码
// 联合体定义
union Data {
    int i;          // 4字节
    float f;        // 4字节
    char str[20];   // 20字节
};  // 注意分号不能省略

// 声明联合体变量
union Data data;  // 声明一个Data类型的变量

// 联合体大小
printf("联合体大小: %lu 字节\n", sizeof(data));  // 输出20(最大成员大小)

联合体成员访问

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

c 复制代码
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;
}

联合体的内存布局

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

c 复制代码
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]: 0x12
    
    printf("m.c[0]: 0x%x\n", m.c[0]);  // 输出0x78(小端序)
    
    return 0;
}

联合体嵌套

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

c 复制代码
// 嵌套联合体的结构体
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)
内存布局 成员依次排列,可能有内存对齐 所有成员共享同一块内存空间
大小计算 所有成员大小之和(考虑对齐) 最大成员的大小
成员访问 所有成员可同时访问 同一时间只能访问一个成员
数据存储 每个成员存储独立的数据 所有成员共享相同的数据存储空间
内存占用 通常较大 通常较小
适用场景 组合不同类型的数据 节省内存,处理变体数据

实际应用示例

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

c 复制代码
#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;
}

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

c 复制代码
#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语言中两种重要的自定义数据类型,它们各自有独特的内存布局和应用场景:

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

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

相关推荐
呆呆的小草33 分钟前
Cesium距离测量、角度测量、面积测量
开发语言·前端·javascript
uyeonashi35 分钟前
【QT系统相关】QT文件
开发语言·c++·qt·学习
冬天vs不冷2 小时前
Java分层开发必知:PO、BO、DTO、VO、POJO概念详解
java·开发语言
sunny-ll2 小时前
【C++】详解vector二维数组的全部操作(超细图例解析!!!)
c语言·开发语言·c++·算法·面试
猎人everest2 小时前
Django的HelloWorld程序
开发语言·python·django
西装没钱买3 小时前
C语言多进程TCP服务器与客户端
服务器·c语言·tcp/ip·进程
嵌入式@秋刀鱼3 小时前
《第四章-筋骨淬炼》 C++修炼生涯笔记(基础篇)数组与函数
开发语言·数据结构·c++·笔记·算法·链表·visual studio code
嵌入式@秋刀鱼3 小时前
《第五章-心法进阶》 C++修炼生涯笔记(基础篇)指针与结构体⭐⭐⭐⭐⭐
c语言·开发语言·数据结构·c++·笔记·算法·visual studio code
别勉.3 小时前
Python Day50
开发语言·python
whoarethenext3 小时前
使用 C/C++的OpenCV 裁剪 MP4 视频
c语言·c++·opencv