目录
8 结构体
8.1 结构体基本概念
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型。
C++内置数据类型:int、float、double、string、char ...
8.2 结构体定义和使用
语法:
struct 结构体名 { 结构体成员列表 };
通过结构体创建变量的方式有3种:
struct 结构体名 变量名
struct 结构体名 变量名 = { 成员1值 , 成员2值...}
定义结构体时,顺便创建变量
示例
#include <iostream>
#include <string>
using namespace std;
//1、创建学生数据类型:学生包括(姓名、年龄、分数)
//自定义数据类型,一些数据类型集合组成的一个类型(其实就是内置数据类型的集合)。
//语法:struct 类型名称 { 成员列表 }
//结构体定义
struct student //结构体名称-见名知意
{
//成员列表---学生属性
string name; //姓名
int age; //年龄
int score; //分数
} stu3; //结构体变量创建方式3
//2、通过学生类型创建具体学生(共有3种方式)
//2.1、struct student s1; // s1就是变量名
//2.2、struct student s2 = {...}; // 创建变量的同时赋初值
//2.3、在定义结构体时顺便创建结构体变量
int main()
{
//【结构体变量创建方式1】2.1、struct student s1;
struct student stu1; //创建结构体时,struct关键字可以省略
//给stu1属性赋值,通过“.”访问结构体变量中的属性
stu1.name = "张三";
stu1.age = 18;
stu1.score = 100;
//方式1打印输出
cout << "姓名:" << stu1.name << " 年龄:" << stu1.age << " 分数:" << stu1.score << endl;
//【结构体变量创建方式2】2.2、struct student s2 = {...};
struct student stu2 = {"李四", 19, 60};
//方式2打印输出
cout << "姓名:" << stu2.name << " 年龄:" << stu2.age << " 分数:" << stu2.score << endl;
//【结构体变量创建方式3】在定义结构体时顺便创建结构体变量,不建议使用
stu3.name = "王五";
stu3.age = 18;
stu3.score = 80;
//方式3打印输出
cout << "姓名:" << stu3.name << " 年龄:" << stu3.age << " 分数:" << stu3.score << endl;
system("pause");
return 0;
}
- 总结1:定义结构体时的关键字是struct,不可省略。
- 总结2:创建结构体变量时,关键字struct可以省略。
- 总结3:结构体变量利用操作符 ''.'' 访问成员。
8.3 结构体数组
作用:将自定义的结构体放入到数组中方便维护。
语法:
struct 结构体名 数组名[元素个数] = { {} , {} , ... {} }
示例
#include <iostream>
#include <string>
using namespace std;
//结构体数组
//1、定义结构体
struct student
{
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
};
int main()
{
//2、创建结构体数组
struct student stuArray[3] =
{
{"张三", 18, 88},
{"李四", 28, 99},
{"王五", 38, 66}
};
//3、给结构体数组中的元素赋值
stuArray[2].name = "赵六";
stuArray[2].age = 66;
stuArray[2].score = 99;
//4、遍历结构体数组
for (int i = 0; i < 3; i++)
{
cout << "姓名:" << stuArray[i].name
<< "\t年龄:" << stuArray[i].age
<< "\t分数:" << stuArray[i].score << endl;
}
system("pause");
return 0;
}
8.4 结构体指针
作用:通过指针访问结构体中的成员。// 利用结构体指针操作结构体成员。
利用操作符
->
可以通过结构体指针访问结构体属性。
示例
&s:取s的地址,返回s对应的数据类型的指针。
#include <iostream>
#include <string>
using namespace std;
//结构体指针
//1、定义学生结构体
struct student
{ //成员列表
string name; //姓名
int age; //年龄
int score; //分数
};
int main()
{
//2、创建学生结构体变量
// struct student stu = {"张三", 25, 100}; //struct可以省略
student stu = {"张三", 25, 99}; //struct可以省略
//3、通过指针指向结构体变量
// struct student *p = &stu; //struct可以省略
student *p = &stu; //struct可以省略
//结构体变量,可以直接通过.来访问
cout << "姓名:" << stu.name << "\t年龄:" << stu.age << "\t分数:" << stu.score << endl;
//4、通过指针访问结构体变量中的数据
p->score = 66; //指针通过 -> 操作符可以访问成员
//通过结构体指针访问结构体中的属性,需要利用“->”
cout << "姓名:" << p->name << "\t年龄:" << p->age << "\t分数:" << p->score << endl;
system("pause");
return 0;
}
- 总结:结构体指针可以通过 -> 操作符 来访问结构体中的成员。
8.5 结构体嵌套结构体
作用: 结构体中的成员可以是另一个结构体。
例如:每个老师辅导一个学员,一个老师的结构体中,记录一个学生的结构体。
示例
#include <iostream>
#include <string>
using namespace std;
// 结构体嵌套结构体
//定义学生结构体
struct student
{
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
};
//定义教师结构体
struct teacher
{
//成员列表
int id; //教师编号
string name; //教师姓名
int age; //教师年龄
struct student stu; //教师辅导的学生 子结构体
};
int main()
{
//创建教师结构体变量
struct teacher t1;
t1.id = 333;
t1.name = "老王";
t1.age = 50;
t1.stu.name = "张三";
t1.stu.age = 18;
t1.stu.score = 99;
cout << "教师编号:" << t1.id
<< "\t教师姓名:" << t1.name
<< "\t教师年龄:" << t1.age
<< "\t教师辅导的学生姓名:" << t1.stu.name
<< endl;
cout << "学生姓名:" << t1.stu.name
<< "\t学生年龄:" << t1.stu.age
<< "\t学生考试分数:" << t1.stu.score
<< endl;
system("pause");
return 0;
}
总结:在结构体中可以定义另一个结构体作为成员,用来解决实际问题。
8.6 结构体做函数参数
作用:将结构体作为参数向函数中传递。
传递方式有2种:
值传递
地址传递
示例
#include <iostream>
#include <string>
using namespace std;
//定义学生结构体
struct student
{
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
};
//打印学生信息函数
//1、值传递
void printStudent1(struct student stu) //(student stu)
{
stu.age = 28; //修改属性
cout << "1子函数中\t姓名:" << stu.name << "\t年龄:" << stu.age << "\t分数:" << stu.score << endl;
}
//2、地址传递
void printStudent2(struct student *stu) //用指针*stu接收地址
{
stu->age = 28;
cout << "2子函数中\t姓名:" << stu->name << "\t年龄:" << stu->age << "\t分数:" << stu->score << endl;
}
int main()
{
//结构体作函数参数
//将学生传入到一个参数中,打印学生身上的所有信息
//创建结构体变量
student stu = {"张三", 18, 100};
stu.name = "李四";
stu.age = 20;
stu.score = 85;
//1、值传递
printStudent1(stu);
cout << "1主函数中\t姓名:" << stu.name << "\t年龄:" << stu.age << "\t分数:" << stu.score << endl;
cout << endl;
//2、地址传递
printStudent2(&stu);
cout << "2主函数中\t姓名:" << stu.name << "\t年龄:" << stu.age << "\t分数:" << stu.score << endl;
system("pause");
return 0;
}
总结:如果不想修改主函数中的数据,用值传递,反之用地址传递。
8.7 结构体中const使用场景
作用:用const来防止误操作。
示例
#include <iostream>
#include <string>
using namespace std;
//学生结构体定义
struct student
{
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
};
/*
* 值传递
* stu.age = 150;//mian中的stu变量不会改变
* 会将实参中的数据拷贝一份,放在形参s上
* 无论形参如何改变,都不会影响实参
* 1、实参有很多属性,每个属性都拷贝一份,拷贝的数据量很大
* 2、假设一个学校有成千上万个人,每个人都调用printStudent函数,拷贝成千上万份数据,
* 这样数据量就会非常大,占用内存空间大
*/
void printStudent1(student stu) // (struct student stu) 省略struct
{
stu.age = 150; //mian中的stu变量不会改变
cout << "姓名:" << stu.name << "\t年龄:" << stu.age << "\t分数:" << stu.score << endl;
}
// void printStudent3(const student stu) // (struct student stu) 省略struct
// {
// stu.age = 150; //mian中的stu变量不会改变
// cout << "姓名:" << stu.name << "\t年龄:" << stu.age << "\t分数:" << stu.score << endl;
// }
//将函数中的形参改为指针,可以减少内存空间的使用,而且不会复制新的副本出来(值传递会复制整个结构体元素)
//const使用场景
void printStudent2(const student *stu) //加const防止函数体中的误操作 指针*stu节省空间,一个指针占4个字节内存
{
//stu->age = 100; //操作失败,因为加了const修饰,常量指针无法修改指针指向的值,只能读不能写。防止age的值会被修改
//加入const之后,一旦有修改的操作就会报错,可以防止我们的误操作(加“const”防止误操作)
cout << "姓名:" << stu->name << "\t年龄:" << stu->age << "\t分数:" << stu->score << endl;
}
int main()
{
//创建结构体变量
student stu = {"张三", 18, 100};
printStudent1(stu); //值传递
//通过函数打印结构体变量信息
printStudent2(&stu); //传入地址,在函数中指针接收地址
cout << "main()中 张三的年龄为:" << stu.age << endl;
system("pause");
return 0;
}
8.8 结构体案例
8.8.1 案例1
案例描述:
学校正在做毕设项目,每名老师带领5个学生,总共有3名老师,需求如下:
- 设计学生和老师的结构体,其中在老师的结构体中,有老师姓名和一个存放5名学生的数组作为成员;
- 学生的成员有姓名、考试分数,创建数组存放3名老师,通过函数给每个老师及所带的学生赋值;
- 最终打印出老师数据以及老师所带的学生数据。
#include <iostream>
#include <string>
#include <ctime>
using namespace std;
//学生结构体
struct Student
{
string sName; //姓名
int score; //分数
};
//老师结构体
struct Teacher
{
string tName; //姓名
struct Student sArray[5]; //学生数组
};
//创建数据——给老师和学生赋值的函数
void allocateSpace(struct Teacher tArray[], int len) //struct可以省略
{
string tName = "老师"; // Teacher_
string sName = "学生";
string nameSeed = "ABCDE";
//给老师进行赋值
for (int i = 0; i < len; i++)
{
tArray[i].tName = tName + nameSeed[i]; //老师姓名
//通过循环给每名老师所带的学生进行赋值
for (int j = 0; j < 5; j++)
{
tArray[i].sArray[j].sName = sName + nameSeed[j];
tArray[i].sArray[j].score = rand() % 61 + 40; // 0~60 40~100
// rand() % 60:0~59
}
}
}
//打印数据——打印所有信息
void printTeachers(struct Teacher tArray[], int len) //struct可以省略
{
for (int i = 0; i < len; i++)
{
cout << "老师的姓名:" << tArray[i].tName << endl;
for (int j = 0; j < 5; j++)
{
cout << "\t学生姓名:" << tArray[i].sArray[j].sName << ";考试分数:" << tArray[i].sArray[j].score << endl;
}
}
}
int main()
{
//利用系统时间,产生随机数
srand((unsigned int)time(NULL)); //随机数种子 头文件 #include <ctime>
//1、创建3名老师的数组
struct Teacher tArray[3]; //老师数组 struct可以省略
//2、通过函数给3名老师的信息赋值,并给老师带的学生信息赋值
cout << "string类型所占内存空间为:" << sizeof(string) << endl; //string类型所占内存空间为:32
cout << "int类型所占内存空间为:" << sizeof(int) << endl; //int类型所占内存空间为:4
cout << "sizeof(tArray):" << sizeof(tArray) << endl; //sizeof(tArray):696
cout << "sizeof(Teacher):" << sizeof(Teacher) << endl; //sizeof(Teacher):232
cout << "sizeof(Student):" << sizeof(Student) << endl; //sizeof(Student):40
cout << "sizeof(tArray[0]):" << sizeof(tArray[0]) << endl; //sizeof(tArray[0]):232
int len1 = sizeof(tArray) / sizeof(Teacher); //计算数组长度
cout << "len1:" << len1 << endl; //len1:3
int len2 = sizeof(tArray) / sizeof(tArray[0]); //计算数组长度
cout << "len2:" << len2 << endl; //len2:3
allocateSpace(tArray, len2); //创建数据
//3、打印所有老师及所带的学生信息
printTeachers(tArray, len2); //打印数据
system("pause");
return 0;
}
8.8.2 案例2
案例描述:
设计一个英雄的结构体,包括成员姓名、年龄、性别;创建结构体数组,数组中存放5名英雄。
通过冒泡排序的算法,将数组中的英雄按照年龄进行升序排序,最终打印排序后的结果。
五名英雄信息如下:
{"刘备", 23, "男"},
{"关羽", 22, "男"},
{"张飞", 20, "男"},
{"赵云", 21, "男"},
{"貂蝉", 19, "女"},
#include <iostream>
#include <string>
using namespace std;
//1、设计英雄结构体
//英雄结构体
struct hero
{
string name; //姓名
int age; //年龄
string sex; //性别
};
//冒泡排序——实现年龄升序排列
void bubbleSort(struct hero arr[], int len) //struct可省
{
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - 1 - i; j++)
{
//如果j下标的元素年龄大于j+1下标的元素的年龄,交换两个元素
if (arr[j].age > arr[j + 1].age)
{
struct hero temp = arr[j]; //临时数据
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
//打印数组——打印排序后数组中的信息
void printHeros(struct hero arr[], int len) //struct可省
{
for (int i = 0; i < len; i++)
{
cout << "姓名:" << arr[i].name << "\t性别:" << arr[i].sex << "\t年龄:" << arr[i].age << endl;
}
}
//1、设计英雄结构体
//2、创建数组存放5名英雄
//3、对数组进行排序,按照年龄进行升序排序
//4、将排序后的结果打印输出
int main()
{
//2、创建数组存放5名英雄
struct hero heroArray[5] =
{
{"刘备", 23, "男"},
{"关羽", 22, "男"},
{"张飞", 20, "男"},
{"赵云", 21, "男"},
{"貂蝉", 19, "女"}};
cout << "string类型所占内存空间为:" << sizeof(string) << endl; //string类型所占内存空间为:32
cout << "int类型所占内存空间为:" << sizeof(int) << endl; //int类型所占内存空间为:4
cout << "sizeof(heroArray):" << sizeof(heroArray) << endl; //sizeof(heroArray):360
cout << "sizeof(heroArray[0]):" << sizeof(heroArray[0]) << endl; //sizeof(heroArray[0]):72
cout << "sizeof(hero):" << sizeof(hero) << endl; //sizeof(hero):72
int len1 = sizeof(heroArray) / sizeof(heroArray[0]); //整体所占空间大小/单个元素所占空间大小
cout << "len1:" << len1 << endl; //len1:5
int len2 = sizeof(heroArray) / sizeof(hero); //获取数组元素个数
cout << "len2:" << len2 << endl; //len1:5
cout << "排序前,数组打印:" << endl;
for (int i = 0; i < len1; i++)
{
cout << "姓名:" << heroArray[i].name << "\t性别:" << heroArray[i].sex << "\t年龄:" << heroArray[i].age << endl;
}
//3、对数组进行排序,按照年龄进行升序排序
bubbleSort(heroArray, len1); //排序
cout << "排序后,数组打印:" << endl;
//4、将排序后的结果打印输出
printHeros(heroArray, len1); //打印
system("pause");
return 0;
}
此案例锻炼的3点:
- 结构体数组的写法
- 冒泡排序 // 使用“冒泡排序”对结构体数组进行排序
- 结构体传入函数的写法、操作结构体的写法