一、基本概念
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型。结构体本质上还是一种数据类型,但它可以包括若干个“成员”,每个成员的类型可以相同也可以不同,也可以是基本数据类型或者又是一个构造类型。
二、结构体的定义和使用
结构体的定义和赋值有三种方式,虽然形式不同,但表示内容都是一样的。
第一种:分别给各个成员变量赋值。赋值方式为:“结构体变量名.成员变量名 = 赋值”,注意赋值时对应的数据类型要正确,比如字符串类型要加引号。
#include<iostream>
using namespace std;
//创建结构体,含有三个参数
struct Student {
string name;
int age;
int score;
};
int main() {
struct Student st1; //struct可以省略
//给st1赋值
st1.name = "张三";
st1.age = 18;
st1.score = 90;
return 0;
}
第二种:使用一条语句给所有成员变量赋值。这种赋值方式为:“结构体变量名 = {各个成员变量值}”。注意各个成员变量之间要用逗号隔开,并注意他们的数据类型。
#include<iostream>
using namespace std;
//创建结构体,含有三个参数
struct Student {
string name;
int age;
int score;
};
int main() {
struct Student st1; //struct可以省略
//给st2赋值
struct Student st2 = { "李四",16,80 };
return 0;
}
第三种:在创建结构体的同时,定义结构体变量名。该变量 st3 也可以采用以上两种方式赋值。
struct Student {
string name;
int age;
int score;
}st3; //创建一个结构体变量st3
三、结构体数组
可以将自定义的结构体放入到数组中方便维护,我们也可以修改结构体里面成员变量的值。
语法:struct 结构体名 数组名[元素个数] = { {},{},…,{} };
举个例子:
#include<iostream>
#include<string>
using namespace std;
struct Student {
string name;
int age;
int score;
};
int main() {
//创建一个结构体数组,包含三个学生的信息
Student stuArray[3] = {
{"胡桃",18,90},
{"夜兰",15,80},
{"纳西妲",12,98}
};
//可以修改结构体数组中元素的值
stuArray[1].age = 20;
//遍历结构体数组输出
for (int i = 0; i < 3; i++) {
cout << stuArray[i].name << " " << stuArray[i].age << " " << stuArray[i].score << endl;
}
return 0;
}
四、结构体指针
可以通过指针访问结构体中的成员,利用操作符“->”可以通过结构体指针访问结构体属性。
哎,遇到了个陌生的符号“->”,其实他的作用就是我们上面使用的 “.” ,只是这里我们改为对指针操作,所以由点号改为箭头。
举个例子:
#include<iostream>
#include<string>
using namespace std;
struct Student {
string name;
int age;
int score;
};
int main() {
//创建一个结构体数组,包含三个学生的信息
Student s = { "张三",18,100 };
//可以修改结构体数组中元素的值
Student* p = &s;
//遍历结构体数组
cout << p->name << " " << p->age << " " << p->score << endl;
return 0;
}
那么可能有疑问了,明明我们点号用的好好的为什么非要加一个指针操作进来呢?他们的区别在哪儿?先别急,这个在下面的“结构体做函数参数”会讲到。在这里你只需要明白结构体指针需要用箭头而不是点号就可以了。
注意,结构体指针的类型是结构体的名字,就是我们只能用 “Student* p = &s” 才能成功接收到结构体 s 中的内容,就像我们平时使用 “int * p = &a” 一样,只是这里的 a 是 int 类型,而我们现在使用的 s 是结构体类型。
五、结构体的嵌套
结构体中的成员可以是一个结构体。
比如:一个老师带一些学生,而每个学生也有自己的属性,那么我们就可以创建一个老师的结构体里面包含学生结构体。
举个例子:这个例子里面包含了前面提到的两种赋值方法,体会一下。
#include<iostream>
#include<string>
using namespace std;
//定义学生结构体
struct student {
int score;
string name;
int age;
};
//定义老师结构体
struct teacher {
int id;
string name;
int age;
struct student stu;//学生结构体
};
int main() {
teacher t;
//一般的赋值方式
t.id = 10001;
t.age = 54;
t.name = "张三";
t.stu.name = "胡桃";
t.stu.age = 19;
t.stu.score = 80;
//这种赋值方式也可以
t={10001,"张三",54,{19,"胡桃",80}};
//这种赋值方式也可以
t.stu={19,"胡桃",80};
return 0;
}
六、结构体做函数参数
将结构体作为参数向函数中传递,传递方式有两种:
1.值传递
2.地址传递
举个例子:可以自己写个输出语句输出对应结果对比试试。
代码里面的注释也解决了前面留下的疑问“点号和箭头的区别是什么”。一个不会改变实参,一个会改变实参。至于他们在实际应用的优劣,需要更多的实践来慢慢体会。
#include<iostream>
#include<string>
using namespace std;
//定义学生结构体
struct student {
string name;
int score;
int age;
};
//1.值传递
//值传递时如果修改形参,不会改变实参。也就是在printStudent01里面修改参数的值的话,main函数里面对应参数的值不变
void printStudent01(student s) {
cout << s.name << " " << s.age << " " << s.score << endl;
}
//2.地址传递
//地址传递时如果修改形参,对应实参也发生改变。就是在printStudent02中修改参数的值,main函数里面对应参数的值也改变
void printStudent02(student * p) {
cout << p->name << " " << p->age << " " << p->score << endl;
}
int main() {
student s = { "胡桃",18,80 };
printStudent01(s);
printStudent02(&s);
return 0;
}
七、结构体中const的使用
作用:防止误操作,可以让程序更安全。
举个例子:假设我们之前已经给printStudents结构体中赋过值,并且printStudents中包含age这个成员变量。
第一种方式传参时没有包含 const ,修改操作不报异常,可以修改;
第二种方式传参时包含了 const ,修改操作无法执行,不可修改。
//1
void printStudents(student * s){
s->age=150;
}
//2
void printStudents(const student * s){
s->age=150;
}
原因:加上 const 之后,指针 s 就变为只读的状态,不可修改。这样就很好地防止了因为误操作而修改了 main 函数中的值从而导致程序崩溃。当然,原因你也可以不用很清楚,会用就好。
八、简单的综合案例
题目:
三名老师带五名学生,要求设计学生和老师的结构体,在老师结构体重,有老师的姓名和一个存放5名学生的数组作为成员。学生的成员变量有姓名和考试分数。创建数组存放3名老师,通过这个函数给每个老师及所带的学生赋值,最终打印出老师的数据以及老师所带学生的数据。
学生和老师的信息通过键盘输入。
由于题目比较简单,只是前面讲到的基础知识的综合练习,就不做讲解直接放代码了。
#include<iostream>
#include<string>
using namespace std;
struct student {
string sName;
int score;
};
struct teacher {
string tName; //姓名
struct student sArray[5]; //定义一个学生的结构体数组
};
//通过这个函数给teacher结构体数组复制
void allocateSpace(teacher tArray[], int len) {
for (int i = 0; i < len; i++) {
cout << "请输入教师的姓名:";
cin >> tArray[i].tName; //输入教师的名字
for (int j = 0; j < 5; j++) {
cout << "请输入学生" << i + 1 << "的姓名和分数:";
cin >> tArray[i].sArray[j].sName; //输入学生的名字
cin >> tArray[i].sArray[j].score; //输入学生的分数
}
}
}
void print(teacher tArray[], int len) {
for (int i = 0; i < len; i++) {
cout << tArray[i].tName << "老师所带的学生和他们的分数为:" << endl;
for (int j = 0; j < 5; j++) {
cout << tArray[i].sArray[j].sName << " " << tArray[i].sArray[j].score << endl;
}
}
}
int main() {
teacher tArray[3];
int len = sizeof(tArray) / sizeof(tArray[0]);
allocateSpace(tArray, len);
print(tArray , len);
return 0;
}
九、重要实例——通讯录管理
这个实例比较重要,对于初学者来说也有一定的难度,不过比较长,放在另一篇文章里讲解。
本篇文章和通讯录管理这两篇文章掌握的话,结构体基本可以过关了,剩下的就需要更多实践来提升能力。