结构体是用户自定义的类型,可以将多种数据的表示合并到一起,描述一个完整的对象。
结构体的存在也就是相当于将多个变量结合在一起进行管理
使用结构体有两个步骤:1)定义结构体描述(类型);2)创建结构体变量。
cpp
定义结构体语法:
struct 结构体名
{
成员一的数据类型 成员名一;
成员二的数据类型 成员名二;
成员三的数据类型 成员名三;
......
成员n的数据类型 成员名n;
};
cpp
创建结构体变量
struct 结构体名 结构体变量名;
struct 结构体名 结构体变量名={成员一的值, 成员二的值,......, 成员n的值};
struct 结构体名 结构体变量名={};
struct 结构体名 结构体变量名={0};❌️,会导致类型不匹配,不要用
清空结构体
1.解释让其={}
2.用memset()函数可以把结构体中全部的成员清零。(只适用于C++基本数据类型)
复制结构体
1.直接用等于号(只适用于C++基本数据类型)。
2.用memcpy()函数把结构体中全部的元素复制到另一个相同类型的结构体(只适用于C++基本数据类型)。
那么为了方便,肯定是使用指针去调用结构体的数据
cpp
(*指针名).成员变量名
指针名->成员变量名 ✅️这个最直观
cpp
#include"iostream"
using namespace std;
struct Student
{
string name;
int age;
int score;
};
int main()
{
Student zs={"jk",18,90};
cout<<"zs name="<<zs.name<<endl;
Student *ptr=&zs;
cout<<"ptr name="<<ptr->name<<endl;
cout<<"(*ptr).name="<<(*ptr).name<<endl;
}
还是那句话,结构体的创建相当于自己创建的一个数据类型,在使用函数进行传参的时候和普通的没有区别。结构体传递给函数,实参取结构体变量的地址,函数的形参用结构体指针。若不想更改结构体的数据增加const约束。
cpp
#include"iostream"
using namespace std;
struct Student
{
string name;
int age;
int score;
};
void changname(Student *p,string newname)
{
p->name=newname;
cout<<"new name="<<p->name<<endl;
}
int main()
{
Student zs={"jk",18,90};
cout<<"zs name="<<zs.name<<endl;
Student *ptr=&zs;
cout<<"ptr name="<<ptr->name<<endl;
cout<<"(*ptr).name="<<(*ptr).name<<endl;
changname(&zs,"lisi");
changname(ptr,"wangwu");
cout<<"zs name="<<zs.name<<endl;
}
用结构体指针指向动态分配的内存的地址。先new再delete
cpp
Student* p = new Student{"lisi", 20, 0}; // 动态创建
cout << p->name << endl; // 访问成员
delete p; // ⚠️ 必须手动释放!
假设数组的数据类型是不是c++基础类型,而是自定义类型,这时候就需要结合数组了,但是也是类似的。
cpp
Student array[3] = {{"lisi", 20, 0}, {"wangwu", 18, 100}, {"zhaoliu", 16, 90}};
for (int i = 0; i < 3; i++) {
cout << array[i].name << ", " << array[i].age << ", " << array[i].score << endl;
}
同样的,结构体里面是可以嵌套的
cpp
struct Point {
double x, y;
};
struct Triangle {
Point vertices[3]; // 三角形由3个点组成
};
int main() {
Triangle tri = {
{{0.0, 0.0}, // vertices[0]
{1.0, 0.0}, // vertices[1]
{0.5, 0.866}} // vertices[2]
};
cout << "三角形三个顶点:\n";
for (int i = 0; i < 3; ++i) {
cout << "(" << tri.vertices[i].x
<< ", " << tri.vertices[i].y << ")\n";
}
}
接下来有个问题,结构体是可以包含指针的,那么这个指针怎么使用、初始化的时候指针是什么状态?
其实结构体中的指针的作用是指向的是动态分配的内存地址。那么你会反应到这就是new,对没错,所以我们初始化的时候就得小心了,因为不控制的话就是随机值 or 0值。这在设计代码的时候会涉及到的。
两种推荐
1.直接初始为0
cpp
struct teacher
{
string name;
int age;
int score;
string* ID;
};
cout<<"--------------------------"<<endl;
teacher t1={};
cout<<"ID="<<t1.ID<<endl;
2.显式初始化,我比较推荐这个
cpp
teacher t2={"t2",18,100,new string("123456")};
cout<<"t2 ID="<<*(t2.ID)<<endl;
delete t2.ID; // 释放动态分配的内存
其实也可以初始化为0,指针为空指针,再单独new。
假如说指针类型指向的数据类型就是自己定义的结构体类型,就构成了链表了。
cpp
struct lbiao
{
int no;
string name;
struct lbiao*next;
};
cout<<"--------------------------"<<endl;
lbiao*head=nullptr,*tail=nullptr,*tmp=nullptr;
tmp=new lbiao{1,"node1",nullptr};
head=tail=tmp;
tmp=new lbiao{6,"node2",nullptr};
tail->next=tmp;
tail=tmp;
tmp = new lbiao({ 3, "node2", nullptr }); // 分配第三个节点。
tail->next = tmp; // 把上一个节点的next指针指向新节点。
tail = tmp; // 移动尾指针,让尾指针指向刚分配出来的新节点。
// 遍历链表。
tmp = head; // 从头节点开始。
while (tmp != nullptr)
{
cout << "no=" << tmp->no << "name=" << tmp->name << "tmp->next=" << tmp->next << endl;
tmp = tmp->next; // 顺着next指向往后面找。
}
// 释放链表。
while (head != nullptr)
{
tmp = head; // 让临时节点指向头节点。
head = head->next; // 头节点后移。
delete tmp; // 删除临时节点。
}
枚举
使用枚举可以表达我们数据的状态。
cpp
枚举的语法:
enum 枚举名 { 枚举量1 , 枚举量2 , 枚举量3, ......, 枚举量n };
例如:
enum colors { red , yellow , blue };
enum colors {red=1,yellow=2,blue=3};
enum colors {red=10,yellow,blue};
值得注意的是用枚举创建的变量取值只能在枚举量范围之内,并且枚举的作用域与变量的作用域相同。