结构体也能排队和套娃:结构体数组与嵌套
困难27结构体也能排队和套娃:结构体数组与嵌套
你是不是遇到过这种麻烦:要记录全班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. 相关指引
掌握了结构体数组和嵌套,你就可以用程序解决更真实的问题了:
- 班级成绩表:每个学生(结构体)有姓名和成绩(数组),再用结构体数组存全班。
- 游戏角色包:角色(结构体)拥有武器(另一个结构体),多个角色组成队伍(结构体数组)。
- 下一步可以学习函数传参:怎么把结构体数组传给函数进行排序、查找?或者动态分配内存(用
new和delete)来创建可变大小的结构体数组。
结构体是C++迈向面向对象编程(类和对象)的基石,学会了它,你就离写出真正的项目更近一步了!
例题精讲
有以下C++结构体定义和数组声明: struct Student { string name; int score; }; Student stu[3] = {{"Alice", 85}, {"Bob", 92}, {"Cindy", 78}}; 以下哪个选项可以正确输出数组 stu 中第二个学生的姓名?
有以下结构体定义: struct Address { string city; string street; }; struct Person { string name; int age; Address addr; }; Person p = {"Tom", 20, {"Beijing", "Haidian"}}; 要访问 Tom 所在的城市,可以使用 p.addr.city。
以下程序定义了一个结构体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;
}设有如下嵌套结构体定义: struct Date { int year; int month; int day; }; struct Event { string name; Date date; }; Event e = {"Party", {2023,12,25}}; 以下哪个选项是访问Event中日期月份的正确方式?
以下程序定义了一个结构体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;
}