1.结构体
是用户自定义的数据类型,这代表了结构体不是一个变量而是一个类型,并不分配空间,并且允许用户存储不同的数据类型,且通常情况使用结构体有以下好处:
1.数据封装:结构体可以将相关的数据项组合在一起,形成一个逻辑上统一的实体,便于表示现实世界中的复杂对象,如学生信息、产品详情等
2.代码可读性和可维护性:通过结构体,可以将数据和相关的操作逻辑组织得更加清晰,使得代码更易于阅读和维护。如果需要改变数据结构,只需调整结构体定义,而不需要修改使用这些数据的所有代码部分
3.模块化编程:结构体可以帮助开发者创建模块化的代码,通过将相关的数据和功能组合在一起,可以更轻松地维护代码并扩展功能
4.内存效率:结构体的成员在内存中是连续存放的,这通常可以提高数据访问的效率,尤其是在使用缓存友好的数据布局时
5.灵活的数据操作:结构体可以作为函数的参数和返回值,允许在不同的函数间传递复杂的数据结构,提高了程序的灵活性
6.支持嵌套和指针:结构体可以包含其他结构体作为成员,或者包含指向自身类型的指针,这为实现更复杂的数据结构如链表、树等提供了便利
7.增强数据类型安全:通过定义结构体,可以创建新的数据类型,从而在编译时期提供更强的类型检查,减少运行时错误
2.创建结构体变量的三种方式:
先创建一个学生类型的结构体:
struct student
{
string name;
int num;
int score;
};
1. struct + 结构体名 + 变量名//创建变量通过点运算符初始化
struct student s1;
s1.name="zhangsan";
2.struct student s1={…};//创建变量顺便初始化
struct student s1={"zhangsan",1,2};
3.创建结构体时顺便创建变量
struct student
{
string name;
int num;
int score;
}s3;
注意:定义结构体时,关键字不能省略,创建结构体变量时可以省略
3.结构体数组
作用:结构体变量较多时,放到数组中方便维护,例如:
struct student//先定义一个结构体类型
{
string name;
int age;
int score;
};
int main
{
struct student stuArr[3];//创建一个结构体数组
Arr[1].name = "李四";//通过点运算符给结构体数组元素赋值
stuArr[3]={{...}.{...},{...}};//或者直接用大括号一次性赋值
}
初始化之后可以用循环遍历结构体数组
for(i=0;i<3;i++)
{
cout<<Arr[i].name<<Arr[i].num<<Arr[i].score<<endl;
}
4.结构体指针
student s1={"zhangsan",18,100};//创建结构体变量
student *p=&s1;//结构体指针指向变量
cout<<p->name<<endl;//通过->访问变量中的数据
5.结构体嵌套结构体
在结构体中可以定义另一个结构体作为成员来解决实际问题
struct student
{
string name;
int num;
int score;
};
struct teacher {
int age;
student s1;//声明了一个student类型的成员变量,但是并没有直接实例化student
}t;
注意:
1.内层结构体一定要先定义好,否则会出现报错
2.内层结构体不可以直接访问,因为内层结构体虽然被嵌套在内部,但是它并没有实例化,所以它不是一个独立的类型,只是teacher的一个成员,因此并不能直接访问student的内部成员,但是我们可以先将外部结构体实例化再进行一步步访问,如果要直接访问student,那么就需要实例化一个独立的student类型变量
cout<<s1.num;错误示范,内层结构体没有单独实例化就不可以直接访问,要实例化一个外部结构体后从外到内一步步访问
3.如果结构体中有一个指向另一个结构体的指针成员,并且想通过这个指针成员访问嵌套结构体的成员,就必须使用箭头而不是点运算符
例如:t.s1->score//合法
t.s1.score//不合法,这里有个teacher的成员s1指向student结构体
int main()
{
student s1;//将内部成员实例化
t.s1.num = 123;//一步步访问内部成员
}
6.结构体做函数参数
struct student//定义结构体
{
string name;
};
1.值传递
void prstud(student s1)
{
cout << "函数调用="<<s1.name<<endl;
}
int main()
{
student s1;//创建结构体变量
s1.name = "李四";//初始化结构体成员
prstud(s1);//结构体做参数调用函数打印出name
}
2.地址传递
void prstud(student* p)
{
cout << "函数调用 ="<<p->name<<endl;//指针访问结构体成员
}
int main()
{
student s1;
s1.name = "李四";
prstud(&s1);//传地址
}
二者区别:地址传递改变的是成员本身,值传递改变的是另外开辟的临时空间,所以如果不想修改主函数的数据,就用值传递
7.const在结构体中的应用
当我们想要使结构体当参数时,如果结构体中数据过多,那么值传递过程中因拷贝浪费的空间就会过多,因此我们可以尝试指针传递,但是指针传递又有可能会在函数操作中修改数据成员的值,为了防止这种情况我们就可以使用常量指针,使成员值只读不写,例如:
void prstud(const student* p)
{
//p->num = 10;如果取消注释就会报错,因为常量指针的变量值不能被修改
cout << "函数调用 ="<<p->num<<endl;
}
int main()
{
student s1 ;
s1.num = 5;
prstud(&s1);
cout << "初始化 =" << s1.num<<endl;
}
注意:
常量指针:指向常量的指针,故指向可改,变量不能改动
指针常量:指针值是常量的指针,故指针值不能改变,变量可以改变
8.结构体的案例
案例一:
学校正在做毕设,3名导师每人带5名学生,要求创建老师和学生的结构体,老师的结构体中有老师姓名和学生结构体数组作为成员,学生结构体中有学生姓名和学生成绩,利用函数给它们的成员都赋初值并打印出来
在这之前需要插入一个字符拼接的知识点因为效率问题这里只介绍两种方便的拼接方式:
1.使用+运算符,即str1+str2=str3
2.使用+=运算符,即str1+=“…”=str2 //此例便用的这个方法
#include<iostream>
#include<string>//c++中的string类头文件
#include<ctime>//时间戳创建随机值
using namespace std;
struct student//创建学生结构体
{
string sname;
int score;
};
struct teacher //创建老师结构体
{
string tname;
student s[5];//学生结构体数组作为内部成员
};
void prt(teacher t[],int n)//打印数据的函数
{
for (int i = 0; i < n; i++)
{
cout << t[i].tname << endl;//打印teacher名字
for (int j = 0; j < 5; j++)//打印5个学生的名字和成绩
cout<<"\t" << t[i].s[j].sname << "\t成绩:" << t[i].s[j].score << endl;
}
}
void f( teacher t[], int n)//为老师的结构体数组遍历赋值
{
string name="abcde";
for (int i = 0; i < n; i++)
{//为名字赋值
t[i].tname = "teacher_";
t[i].tname += name[i];//字符拼接
for (int j = 0; j < 5; j++)//为学生赋值
{
t[i].s[j].sname = "student_";
t[i].s[j].sname += name[j];
t[i].s[j].score = rand() % 101;
}
}
}
int main()
{
srand((unsigned int)time(NULL));
teacher t[3];
int n = sizeof t / sizeof t[0];
f(t, n);
prt(t,n);
return 0;
}
案例二:
创建一个长度为5的英雄的结构体类型,其中包含了姓名,年龄,性别的属性,随机初始化五个英雄,用冒泡排序法对他们的年龄进行排序,最后将他们全部打印出来
struct hero
{
string name;
int age;
string sex;
};
void agesort(hero a[], int n)
{
for(int i=0;i<4;i++)
for (int j = 0; j < 4 - i; j++)
if (a[j].age > a[j + 1].age) { hero b = a[j]; a[j] = a[j + 1]; a[j + 1] = b; }
}
int main()
{//初始化数组:
hero a[5] = { {"刘备",23,"男"} ,{"关羽",22,"男"},{"张飞",20,"男"},{"赵云",21,"男"},{"貂蝉",19,"女"} };
int n = sizeof a / sizeof a[0];
agesort(a, n);//调用冒泡排序函数
for (int i = 0; i < 5; i++)//打印
{
cout << a[i].name << "\t" << a[i].age << "\t" << a[i].sex << endl;
}
}