Skip to content

结构体与联合体

约 3639 字大约 12 分钟

2025-06-25

结构体

结构体

在C++中,结构体(struct 是一种用户自定义的数据类型,用于将不同类型的数据组合成一个整体,以表示具有多个属性的复杂对象(如学生、点、矩形等)。在信奥赛中,结构体是处理多属性数据的核心工具,广泛应用于存储坐标、记录信息、定义复杂数据模型等场景。

一、结构体的定义与基本用法

结构体的定义与基本用法

结构体通过struct关键字定义,可包含多个不同类型的成员变量(属性)。

1. 定义语法

c++
struct 结构体名 {
    数据类型 成员变量1;
    数据类型 成员变量2;
    // ... 更多成员变量
};  // 注意末尾的分号

2. 示例:定义“学生”结构体

c++
// 定义一个表示学生的结构体
struct Student {
    string name;  // 姓名(string类型)
    int age;      // 年龄(int类型)
    double score; // 成绩(double类型)
};  // 分号不可省略

二、结构体变量的创建与初始化

结构体变量的创建与初始化

定义结构体后,需创建结构体变量才能使用,初始化方式灵活。

1. 声明与初始化

c++

2. 访问成员变量

通过点运算符(. 访问结构体变量的成员:

c++
cout << "姓名:" << s1.name << endl;    // 输出Alice
cout << "年龄:" << s1.age << endl;     // 输出15
cout << "成绩:" << s1.score << endl;   // 输出92.5

三、结构体数组

结构体数组

结构体数组用于存储多个同类型的结构体对象(如多个学生、多个点),是信奥赛中批量处理复杂数据的常用方式。

1. 定义与初始化

c++
// 定义学生数组(存储3个学生)
Student class1[3] = {
    {"Alice", 15, 92.5},
    {"Bob", 16, 88.0},
    {"Charlie", 15, 95.5}
};

2. 访问数组元素的成员

通过“数组下标+点运算符”访问:

c++
// 遍历学生数组
for (int i = 0; i < 3; i++) {
    cout << "" << i+1 << "名学生:" 
         << class1[i].name << "" 
         << class1[i].age << "岁," 
         << class1[i].score << "" << endl;
}

四、结构体与函数

结构体与函数

结构体可作为函数的参数、返回值,实现复杂数据的传递和处理。

1. 结构体作为函数参数(传值)

c++
// 函数:输出学生信息(传值,不修改原始数据)
void printStudent(Student s) {
    cout << "姓名:" << s.name << ",年龄:" << s.age << ",成绩:" << s.score << endl;
}

// 调用
printStudent(s1);  // 传递结构体变量s1

2. 结构体作为函数参数(传引用,高效且可修改)

c++
// 函数:提高学生成绩(传引用,直接修改原始数据)
void improveScore(Student& s, double add) {
    s.score += add;  // 修改成员变量
}

// 调用
improveScore(s1, 5.5);  // s1.score变为98.0

3. 结构体作为函数返回值

c++
// 函数:创建并返回一个学生对象
Student createStudent(string name, int age, double score) {
    Student s;
    s.name = name;
    s.age = age;
    s.score = score;
    return s;
}

// 调用
Student s4 = createStudent("David", 17, 85.0);

五、结构体的进阶用法

结构体的进阶用法

1. 结构体嵌套

结构体成员可以是另一个结构体(表示更复杂的对象):

c++

2. 结构体中的函数(成员函数)

C++结构体可以包含函数(成员函数),用于处理结构体自身的数据:

c++

六、信奥赛核心应用

信奥赛核心应用

1. 存储坐标与几何计算

c++

2. 排序结构体数组

通过自定义排序规则(需结合sort函数和比较函数):

c++

3. 记录复杂数据(如事件、任务)

c++
struct Event {
    string name;   // 事件名称
    int time;      // 时间(分钟)
    bool isUrgent; // 是否紧急
};

// 筛选紧急事件
void printUrgentEvents(Event events[], int n) {
    for (int i = 0; i < n; i++) {
        if (events[i].isUrgent) {
            cout << events[i].name << "(时间:" << events[i].time << "" << endl;
        }
    }
}

七、避坑指南

避坑指南

  1. 结构体定义后的分号
    结构体定义末尾必须加;,否则会编译错误:

    c++
    struct Point { int x, y; }  // 错误:缺少分号
    struct Point { int x, y; }; // 正确
  2. 未初始化的成员变量
    结构体变量的成员若未初始化,值为随机垃圾值(尤其全局结构体数组会默认初始化):

    c++
    Student s;
    // cout << s.score;  // 错误:未初始化,值不确定
  3. 结构体作为参数的效率
    传值方式会复制整个结构体(大结构体效率低),建议用传引用(const引用用于只读):

    c++
    // 高效且安全的只读访问
    void printStudent(const Student& s) {  // const确保不修改
        cout << s.name << endl;
    }
  4. 结构体数组的初始化
    部分初始化时,未指定的成员会默认初始化(数值为0,字符串为空):

    c++
    Student class2[2] = {{"Alice", 15}};  // 第二个学生:name空,age=0,score=0.0

八、实战:结构体数组排序与统计

结构体数组排序与统计

题目:定义学生结构体,输入5名学生的姓名和成绩,按成绩降序排序后输出,并统计及格人数。

c++

重要

结构体是信奥赛中组织复杂数据的核心工具,通过将多个相关属性封装为一个整体,能显著提高代码的可读性和逻辑性。掌握结构体的定义、初始化、成员访问,以及结构体数组和函数的结合使用,是解决几何问题、数据统计、排序等场景的基础。在实际解题中,合理设计结构体成员和相关函数,能高效处理多维度数据。

联合体

联合体(union

在C++中,联合体(union 是一种特殊的数据类型,它允许在同一块内存空间中存储不同类型的数据,但同一时间只能有一个成员有效。与结构体(struct)不同,联合体的所有成员共享同一段内存,这使得它在内存受限场景或需要类型转换时非常有用。

一、联合体的定义与基本特性

联合体的定义与基本特性

1. 定义语法

c++
union 联合体名 {
    数据类型 成员1;
    数据类型 成员2;
    // ... 更多成员
};

2. 核心特性

  • 内存共享:所有成员共用同一块内存空间,联合体的大小等于其最大成员的大小。
  • 互斥访问:同一时间只能使用一个成员,修改一个成员会覆盖其他成员的值。

示例

c++

二、联合体与结构体的区别

特性联合体(union结构体(struct
内存分配所有成员共享同一块内存,大小为最大成员的大小成员按顺序分配内存,总大小为各成员大小之和(可能有对齐)
成员访问同一时间只能有效访问一个成员,修改会覆盖其他成员所有成员可同时访问,修改一个不影响其他成员
用途节省内存、类型转换、存储互斥数据组合不同类型数据,表示一个完整对象(如学生、点)

三、联合体的应用

联合体的应用

1. 节省内存(存储互斥数据)

当数据项互斥(即不会同时使用)时,用联合体可大幅减少内存占用:

c++
// 表示一个"值",可能是整数或字符串(但不会同时是两者)
union Value {
    int num;
    char str[20];  // 假设字符串最长19字符(+结束符)
};

// 若用结构体,大小为4+20=24字节;用联合体,大小为20字节(取最大成员)

2. 类型转换(访问内存的不同方式)

通过联合体可查看同一内存区域的不同类型表示(如将整数的字节拆分):

c++

3. 与结构体结合(标记联合体)

通过结构体标记联合体当前使用的成员,避免访问错误:

c++

四、信奥赛中的应用

信奥赛中的应用

联合体在信奥赛中虽不如结构体常用,但在特定场景下可简化代码:

  1. 内存敏感的嵌入式场景:如限制内存使用的题目,存储互斥数据时减少内存占用。
  2. 位运算与内存解析:如解析二进制文件格式(将字节流转换为不同类型数据)。
  3. 类型转换技巧:如快速获取浮点数的二进制表示(用于特定算法优化)。

五、避坑指南

避坑指南

  1. 成员互斥性
    绝对不能同时访问联合体的多个成员,修改一个成员后,其他成员的值会变得不确定(可能是垃圾值):

    c++
    union U { int a; float b; };
    U u;
    u.a = 10;
    u.b = 3.14;
    // cout << u.a;  // 错误:a已被b覆盖,值无意义
  2. 字符串等复杂类型
    联合体中避免使用string等非POD(Plain Old Data)类型(C++标准不允许,部分编译器可能支持但不安全),应使用字符数组:

    c++
    union Bad {
        string s;  // 错误:string包含构造/析构函数,不能放在联合体中
        int i;
    };
    
    union Good {
        char s[20];  // 正确:字符数组是POD类型
        int i;
    };
  3. 初始化与赋值
    联合体只能初始化第一个成员,或通过显式指定初始化:

    c++
    union U { int a; float b; };
    U u1 = {10};  // 正确:初始化第一个成员a
    U u2 = {.b = 3.14f};  // 正确:C++11后支持指定成员初始化

六、实战:判断系统字节序

判断系统字节序

字节序是数据在内存中的存储顺序(小端:低字节存低地址;大端:高字节存低地址),可用联合体判断:

c++

说明:多数PC(如x86架构)是小端字节序,部分嵌入式设备是大端字节序。

重要

联合体是C++中一种特殊的数据共享机制,核心价值在于内存复用。虽然在信奥赛中应用场景不如结构体广泛,但理解其原理和用法,能在处理互斥数据、类型转换等场景中写出更高效的代码。使用时需严格遵守“同一时间只访问一个成员”的规则,避免逻辑错误。