结构概述
结构体类型将一些分量聚合成一个整体,用一个变量表示。
一个结构体的各个分量都有名字,这些分量称为成员(member)。
由于结构体的成员可以是各种类型的(相当于数据打包),程序员能创建适合于问题的数据聚合。
其实,后面将要学习的类也可以实现数据打包,但相比结构体,类还实现了操作打包。
结构体的使用
- 定义一个结构体类型
- 定义结构体类型的变量
- 访问结构体变量
结构体变量
我们先来看一个例子:
定义学生结构体,包含姓名、学号、出生日期、英语成绩、数学成绩。
定义结构体变量,输入学生信息,输出所有信息及平均成绩。
#include <iostream>
#include <cstring>
using namespace std;
// 定义学生结构体,包含姓名、学号、出生日期、英语成绩、数学成绩。
// 定义结构体变量,输入学生信息,输出所有信息及平均成绩。
struct date
{
int year,month,day;
};
struct student
{ //记住结构体的定义方式
int no;
string name; //string类型
date birthday; //结构体里面还可以嵌套结构体date
int eng,math;
};
int main()
{
//输入:202115 小明 2000 08 25 90 100
/*——————————————————————————————————————————————————————————————————————————————————
//方式一:使用cin、cout输入输出
student s; //定义结构体变量s
cin >> s.no >> s.name
>> s.birthday.year >> s.birthday.month >> s.birthday.day
>> s.eng >> s.math;
cout << s.no << " " << s.name << " "
<< s.birthday.year << " " << s.birthday.month << " " << s.birthday.day << " "
<< s.eng << " " << s.math << endl; // 输出:202115 小明 2000 08 25 90 100
*///——————————————————————————————————————————————————————————————————————————————————
//方式二:定义的同时赋值初始化
student s = { 202115,"lisi", {2002,1,1},90, 100 }; //注意:必须加上=
cout << sizeof(s) << endl; //输出:56(是所有字段的字节数之和)
cout << sizeof(string) << endl; //输出:24
cout << s.no << " " << s.name << " "
<< s.birthday.year << " " << s.birthday.month << " " << s.birthday.day << " "
<< s.eng << " " << s.math << endl; //输出:202115 lisi 2002 1 1 90 100
return 0;
}
这个例子包含了以下知识点:
(1)结构体变量的定义
结构体变量的定义和普通的变量定义一样。
格式:结构体类型名 变量
一旦定义了一个结构体类型的变量,系统在分配内存时就会分配一块连续的空间,依次存放它的每一个分量。这块空间总的名字就是结构体变量的名字。内部还有各自的名字。
如上述例子使用sizeof可以看出结构体是所有字段的字节数之和。
(2)结构体变量的初始化
student s = { "2021","lisi",2002,1,1,90,100 };
student s = { "2021","lisi",{2002,1,1},90,100 };
(3)变量的访问
对结构体类型变量的引用一般为引用它的成员
格式:变量名.成员名
如 s.name
若成员中还包含结构体,可逐级展开,例如:s.birthday.year
(4) 结构体变量的赋值
结构体变量的赋值通常是通过对它的每一个成员的赋值而实现。如上述的cin格式。
结构体变量的输出通常是通过输出它的每一个成员而实现。如上述的cout格式。
注意:同类型的结构变量之间可以相互赋值。例如:假设两个学生结构体变量s1,s2。s1 = s2;
表示将s2的成员对应赋给s1的成员。
结构与指针(结构体指针)
指向结构的指针
直接定义指针变量并给结构体指针赋值:student *sp = & s1;
结构体指针变量的引用
- (*指针).成员。 例如:(*sp).name
- 指针->成员。 例如:sp->name ( ->是所有运算符中优先级最高的,通常使用这种方法)
通过指针动态分配结构体空间
和申请普通的动态变量一样。
student *sp;
sp = new student; // 分配一个空间
sp = new student[n]; // 分配n个空间
接下来看一个详细的例子:
#include <iostream>
#include <cstring>
using namespace std;
struct date
{
int year,month,day;
};
struct student
{
int no;
string name;
date birthday;
int eng,math;
};
int main()
{
//结构体变量
student s = { 202115,"lisi", {2002,1,1},90, 100 };
student* ps = &s; //通过指针访问结构体
//直接访问方式
cout << s.no << " " << s.name << " " << s.birthday.year
<< " " << s.birthday.month << " " << s.birthday.day
<< " " << s.eng << " " << s.math; //输出:202115 lisi 2002 1 1 90 100
cout<<endl;
//通过指针进行访问
cout << (*ps).no << " " << (*ps).name << " " << ps->birthday.year //第一种访问方式:ps是指针,*ps是值进行访问
<< " " << ps->birthday.month << " " << ps->birthday.day //第二种访问方式:直接通过ps指针访问,使用ps->
<< " " << ps->eng << " " << ps->math; //输出:202115 lisi 2002 1 1 90 100
return 0;
}
结构与数组(结构体数组)
引用数组的某一成员的成员
studentArray[3].name
数组成员之间相互赋值
studentArray[4] = studentArray[2]
结构数组的初始化
student studentArray[5] = { {“00001”, “lisi”, 2002,1,1,90,98 }, {…}, {…}, {…}};
结构体数组的输入输出:先定义,后循环输入输出
#include <iostream>
#include <cstring>
using namespace std;
struct date
{
int year,month,day;
};
struct student
{
int no;
string name;
date birthday;
int eng,math;
};
int main()
{
//方式一:定义结构体数组并初始化
student s[3] = { 20211501,"lisi", {2002,1,1},90, 100 ,
20211502,"wangwu", {2002,1,1},89, 96,
20211503,"zhaoliu", {2002,1,1},90, 95 };
int i;
//方式二:使用cin循环输入结构体数组
for (i = 0; i < 3; i++)
{
cin >> s[i].no >> s[i].name >> s[i].birthday.year
>> s[i].birthday.month >> s[i].birthday.day >> s[i].eng >> s[i].math;
}
//循环输出结构体数组
for (i = 0; i < 3; i++)
{
cout << s[i].no << " " << s[i].name << " " << s[i].birthday.year
<< " " << s[i].birthday.month << " " << s[i].birthday.day
<< " " << s[i].eng << " " << s[i].math << endl;
}
return 0;
}
结构与参数传递,返回结构体
结构体类型是数据类型,作为参数传递,无论传值、传指针、传引用和数组,和传递基本类型参数语法相同。
返回结构体,同返回基本数据类型语法相同,可以返回结构体值、返回结构体引用、返回结构体指针。
需要注意的是:返回引用、指针,需要静态(static)局部变量或者动态内存分配空间,确保函数执行完空间仍在。
单变量
#include <iostream>
#include <cstring>
using namespace std;
struct date
{
int year,month,day;
};
struct student
{
int no;
string name;
date birthday;
int eng,math;
};
//void output11(student s); //一般不用。传结构体的值,值传递是备份,耗费空间,所以一般很少用值传递,所以一般传引用
void input1(student& s); //传结构体的引用(单变量)
void output1(const student& s);
student input2(); //返回结构体值
int main()
{
student s;
//函数传结构体 引用,值,指针
input1(s);
output1(s); //输出成功:202115 lisi 2002 1 1 90 100
//返回结构体
s = input2();
output1(s); //输出成功:202115 lisi 2002 1 1 90 100
return 0;
}
//——————————————————————————————————————————————————————————————————————————————
//input1 (单变量)传结构体引用。因为要改变参数的值,所以传引用
void input1(student& s)
{
cin >> s.no >> s.name >> s.birthday.year
>> s.birthday.month >> s.birthday.day >> s.eng >> s.math;
}
//void output11(student s) 因为输出不改变参数的值,所以直接传值即可,但由于传值是备份,增加空间,所以使用const引用,表示不改变参数值的引用
//output1 (单变量)传结构体引用。输出不改变参数值
void output1(const student& s)
{
cout << s.no << " " << s.name << " " << s.birthday.year
<< " " << s.birthday.month << " " << s.birthday.day
<< " " << s.eng << " " << s.math;
}
//——————————————————————————————————————————————————————————————————————————————
//intput2 返回结构体值
student input2()
{
student s;
cin >> s.no >> s.name >> s.birthday.year
>> s.birthday.month >> s.birthday.day >> s.eng >> s.math;
return s;
}
//——————————————————————————————————————————————————————————————————————————————
//单变量输入示例: 202115 lisi 2002 1 1 90 100
数组
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
struct date
{
int year,month,day;
};
struct student
{
int no;
string name;
date birthday;
int eng,math;
};
void input3(student* s, int n); //传结构体数组(多变量数组)
void output3(student* s, int n);
void sort1(student* s, int n); //方式一:自己写的sort函数;也可以使用方式二:调用系统sort函数
int main()
{
student s[3];
//传结构体数组
input3(s, 3);
sort1(s, 3); //方式一:自己写的冒泡排序函数
//sort(s, s + 3, cmp); //方式二:调用系统sort函数。s表示起始元素;s+3表示最后一个元素,由于是半开区间,[s,s+3);cmp是自己写的比较函数,表示排序规则;
output3(s, 3);
return 0;
}
//——————————————————————————————————————————————————————————————————————————————
//函数传指针,也改变参数的值
void input3(student* s, int n)
{
int i;
for (i = 0; i < 3; i++)
{
cin >> s[i].no >> s[i].name >> s[i].birthday.year
>> s[i].birthday.month >> s[i].birthday.day >> s[i].eng >> s[i].math;
}
}
void output3(student* s, int n)
{
int i;
for (i = 0; i < 3; i++)
{
cout << s[i].no << " " << s[i].name << " " << s[i].birthday.year
<< " " << s[i].birthday.month << " " << s[i].birthday.day
<< " " << s[i].eng << " " << s[i].math << endl;
}
}
//——————————————————————————————————————————————————————————————————————————————
//方式一:自己写的冒泡排序函数
void sort1(student* s, int n)
{
for (int i = 1; i < n; i++)
for (int j = 0; j < n - i; j++)
{
//以英语和数学成绩的平均值进行升序排序
if ((s[j].eng + s[j].math) / 2 > (s[j + 1].eng + s[j + 1].math) / 2)
{
/*student temp; //结构体可以当作变量使用,所以不用写交换语句,可以使用swap进行整体交换,
temp = s[j];
s[j] = s[j + 1];
s[j + 1] = temp;*/
swap(s[j], s[j + 1]); //swap交换函数对结构体也是适用的,记得头文件algorithm
}
}
}
//——————————————————————————————————————————————————————————————————————————————
//方式二:调用系统sort函数,但由于系统不知道排序规则是什么,所以需要自己写一个排序规则的比较函数。
bool cmp(const student& lhs, const student& rhs) // lhs rhs 两个变量表示前后两个操作数;如果顺序不对,就要进行交换
{
//英语和数学成绩的平均升序
// return (lhs.eng + lhs.math) / 2 < (rhs.eng + rhs.math) / 2;
//按照出生年月日进行排序
/*return lhs.birthday.year * 1000 + lhs.birthday.month * 100 + lhs.birthday.day
< rhs.birthday.year * 1000 + rhs.birthday.month * 100 + rhs.birthday.day;
*/
//英语成绩升序,若英语相同,数学成绩降序。
if (lhs.eng == rhs.eng)
return lhs.math > rhs.math;
return lhs.eng < rhs.eng;
}
/* 多变量数组输入示例:
20211501 lisi 2002 12 31 100 100
20211502 wangwu 2001 3 4 100 96
20211503 zhaoliu 2002 1 1 90 95
*/