CC++ & Algorithm

结构体也能排队和套娃:结构体数组与嵌套

困难27
语言版本:C++Python(暂无)
概述:学会把结构体放到数组里组成“表格”,以及在一个结构体里嵌套另一个结构体,处理更复杂的数据关系。

结构体也能排队和套娃:结构体数组与嵌套

你是不是遇到过这种麻烦:要记录全班40个同学的名字、年龄、学号,总不能一个个定义40个结构体变量吧?或者,每个同学还有生日(年、月、日),难道要在结构体里写上三个单独的变量?别急,C++ 里两个“小技巧”能帮你轻松搞定——结构体数组让一堆结构体排成队,结构体嵌套让结构体里面再套一个结构体,就像套娃一样。这样,你的数据就能组织得清清楚楚,处理起来也特别方便。


1. 结构体数组:让数据排好队

概念:结构体数组就是一个数组,里面的每个元素都是一个结构体。比如你要存全班同学的信息,直接定义一个“学生数组”,每个位置放一个学生。

生活例子:想象你有一个能放30个收纳盒的架子,每个盒子里贴着一位同学的姓名、年龄、学号。你要找第5个同学,直接走到第5个盒子前就行,不用翻遍所有盒子。

定义和使用

#include <iostream>
#include <string>
using namespace std;

// 定义学生结构体
struct Student {
    string name;   // 姓名
    int age;       // 年龄
    int id;        // 学号
};

int main() {
    // 声明一个能放3个学生的数组,就像3个抽屉排成一排
    Student classRoom[3];

    // 给每个抽屉放入数据,注意顺序要跟结构体定义一致
    classRoom[0] = {"小明", 10, 1};   // 第一个学生
    classRoom[1] = {"小红", 9, 2};    // 第二个学生
    classRoom[2] = {"小刚", 11, 3};   // 第三个学生

    // 用循环遍历数组,一次性输出所有学生
    for (int i = 0; i < 3; i++) {
        cout << "学号" << classRoom[i].id << ":"
             << classRoom[i].name << ","
             << classRoom[i].age << "岁" << endl;
    }

    return 0;
}

输出:

学号1:小明,10岁
学号2:小红,9岁
学号3:小刚,11岁

要点

  • 像普通数组一样声明:Student classRoom[3];
  • 赋值时用花括号 {} 给每个元素一次性初始化。
  • 数组名[下标].成员 访问某个学生的某个字段。
  • for 循环处理所有学生特别方便,不用写重复代码。

2. 结构体嵌套:数据也能套娃

概念:一个结构体里的成员可以是另一个结构体类型的变量。比如“学生”有生日,而生日本身又包含年、月、日,就可以先定义日期结构体,再把它作为学生的一个成员。

生活例子:一张学生档案卡上,“出生日期”那一栏不是单独写一行,而是再分成“年、月、日”三个小格子。这就像一个大信封里还装了一个小信封。

定义和使用

#include <iostream>
#include <string>
using namespace std;

// 先定义日期结构体
struct Date {
    int year;   // 年
    int month;  // 月
    int day;    // 日
};

// 再定义学生结构体,里面嵌套日期结构体
struct Student {
    string name;       // 姓名
    int age;           // 年龄
    Date birthday;     // 生日(嵌套另一个结构体)
};

int main() {
    // 方法1:先声明变量,再一个个赋值
    Student stu1;
    stu1.name = "小明";
    stu1.age = 10;
    stu1.birthday.year = 2014;
    stu1.birthday.month = 5;
    stu1.birthday.day = 20;

    cout << stu1.name << " 出生于 "
         << stu1.birthday.year << "年"
         << stu1.birthday.month << "月"
         << stu1.birthday.day << "日" << endl;

    // 方法2:定义时直接初始化(嵌套的大括号不能少)
    Student stu2 = {"小红", 9, {2015, 3, 10}};

    cout << stu2.name << " 出生于 "
         << stu2.birthday.year << "年"
         << stu2.birthday.month << "月"
         << stu2.birthday.day << "日" << endl;

    return 0;
}

输出:

小明 出生于 2014年5月20日
小红 出生于 2015年3月10日

访问嵌套成员:用多个点号连接,比如 stu1.birthday.year。从外到内一层层点下去,就像开锁一样。

初始化嵌套结构体:在花括号里再写一层花括号,内层对应嵌套结构体的成员。比如 Student stu2 = {"小红", 9, {2015, 3, 10}}; 里面的 {2015, 3, 10} 就是给 birthday 这个日期结构体赋值的。


3. 新手容易犯的错误

  • 忘记结构体定义后面的分号struct Student { ... }; 最后的 ; 很容易漏掉。少了它,编译会报错说“expected ‘;’ after struct definition”。

  • 嵌套初始化时大括号层数不对:比如 Student stu2 = {"小红", 9, 2015, 3, 10};,这样编译器会把 2015 当成某个普通成员,导致类型不匹配。正确写法是需要内层大括号 {2015, 3, 10}

  • 结构体数组下标越界:比如定义了 Student classRoom[3];,却试图访问 classRoom[3](下标从0开始,最大是2)。程序会访问到未知内存,可能崩溃或输出奇怪数据。

  • 访问嵌套成员时点号写少或写多:比如写成 stu1.birthday 而不是 stu1.birthday.year,会把整个日期结构体输出(但cout不知道如何输出结构体),导致编译错误。记住:你想拿到具体哪个字段,就点到底。

  • 结构体定义顺序颠倒:如果嵌套的结构体还没定义就使用,比如先写 Student 再写 Date,编译器不认识 Date。一般先定义内层结构体,再定义外层。


4. 完整可运行的示例:图书与书架

结合小练习,写一个完整的程序:先定义“图书”结构体(书名、作者、出版年份),再定义“书架”结构体(内含一个最多能放10本书的数组和当前图书数量),然后在main函数里创建书架并添加几本书,最后输出书架上的所有书。

#include <iostream>
#include <string>
using namespace std;

// 图书结构体
struct Book {
    string title;   // 书名
    string author;  // 作者
    int pubYear;    // 出版年份
};

// 书架结构体(包含图书数组和当前数量)
struct Bookshelf {
    Book books[10]; // 最多放10本书的数组
    int count;      // 当前有几本书
};

int main() {
    // 创建一个书架变量,并初始化:一开始没有书
    Bookshelf myShelf;
    myShelf.count = 0;

    // 添加第一本书
    myShelf.books[0] = {"哈利波特与魔法石", "J.K.罗琳", 1997};
    myShelf.count++;  // 书数量加1

    // 添加第二本书
    myShelf.books[1] = {"三体", "刘慈欣", 2008};
    myShelf.count++;

    // 用循环输出书架上的所有书
    cout << "我的书架上有" << myShelf.count << "本书:" << endl;
    for (int i = 0; i < myShelf.count; i++) {
        cout << "《" << myShelf.books[i].title << "》"
             << " 作者:" << myShelf.books[i].author
             << " 出版年份:" << myShelf.books[i].pubYear << endl;
    }

    return 0;
}

输出:

我的书架上有2本书:
《哈利波特与魔法石》 作者:J.K.罗琳 出版年份:1997
《三体》 作者:刘慈欣 出版年份:2008

这个例子把结构体数组和嵌套都用上了:Bookshelf 里嵌套了一个 Book 结构体数组。你可以试着给书架增加删除、查找书籍的功能,或者把书架也放在数组里变成“图书馆”结构体。


5. 相关指引

掌握了结构体数组和嵌套,你就可以用程序解决更真实的问题了:

  • 班级成绩表:每个学生(结构体)有姓名和成绩(数组),再用结构体数组存全班。
  • 游戏角色包:角色(结构体)拥有武器(另一个结构体),多个角色组成队伍(结构体数组)。
  • 下一步可以学习函数传参:怎么把结构体数组传给函数进行排序、查找?或者动态分配内存(用 newdelete)来创建可变大小的结构体数组。

结构体是C++迈向面向对象编程(类和对象)的基石,学会了它,你就离写出真正的项目更近一步了!

例题精讲

1单选题

有以下C++结构体定义和数组声明: struct Student { string name; int score; }; Student stu[3] = {{"Alice", 85}, {"Bob", 92}, {"Cindy", 78}}; 以下哪个选项可以正确输出数组 stu 中第二个学生的姓名?

Acout << stu[1].name;
Bcout << stu[2].name;
Ccout << stu[1]->name;
Dcout << stu[2]->name;
2判断题

有以下结构体定义: struct Address { string city; string street; }; struct Person { string name; int age; Address addr; }; Person p = {"Tom", 20, {"Beijing", "Haidian"}}; 要访问 Tom 所在的城市,可以使用 p.addr.city。

3填空题
以下程序定义了一个结构体Student,并创建了一个结构体数组stus。请补充代码,实现按照score成员从高到低排序(冒泡排序)。

#include <iostream>
using namespace std;
struct Student {
    string name;
    int score;
};
int main() {
    Student stus[4] = {{"A",88},{"B",76},{"C",95},{"D",82}};
    for(int i=0; i<3; i++) {
        for(int j=0; j<3-i; j++) {
            if( ___ ) {   // 填空处
                Student temp = stus[j];
                stus[j] = stus[j+1];
                stus[j+1] = temp;
            }
        }
    }
    for(int i=0; i<4; i++)
        cout << stus[i].name << " ";
    return 0;
}
4单选题

设有如下嵌套结构体定义: struct Date { int year; int month; int day; }; struct Event { string name; Date date; }; Event e = {"Party", {2023,12,25}}; 以下哪个选项是访问Event中日期月份的正确方式?

Ae.date.month
Be.month
Cdate.month
De.date[1]
5填空题
以下程序定义了一个结构体Point,并创建了一个结构体数组points,要求补充代码实现从键盘输入5个点的坐标(x和y),然后找出距离原点(0,0)最近的点并输出其坐标。距离公式:sqrt(x*x+y*y)。

#include <iostream>
#include <cmath>
using namespace std;
struct Point {
    int x;
    int y;
};
int main() {
    Point points[5];
    for(int i=0; i<5; i++) {
        cin >> points[i].x >> ___ ;  // 填空1
    }
    int minIndex = 0;
    double minDist = sqrt(points[0].x*points[0].x + points[0].y*points[0].y);
    for(int i=1; i<5; i++) {
        double dist = sqrt(points[i].x*points[i].x + points[i].y*points[i].y);
        if( ___ ) {  // 填空2
            minDist = dist;
            minIndex = i;
        }
    }
    cout << "Closest point: (" << points[minIndex].x << "," << points[minIndex].y << ")";
    return 0;
}