结构体
约 3112 字大约 10 分钟
2025-06-21
1.结构体的使用
1.1.声明结构体类型
结构体是一些类型的集合,通过这个结构体可以让用户自定义出一个变量类型,以描述一个更加复杂的容器,存储更加复杂的数据。可以使用 struct 来声明一个结构体类型,利用这个结构体类型来声明结构体变量。
// 声明结构体类型, 并且创建一个结构体变量
struct str
{
int a;
char b;
double c;
};
int main()
{
struct str s; // 可以将 struct str 整体理解为一个变量类型, 和 int、double 等在地位上是等价的
return 0;
}警告
警告:结构体声明的最后有一个分号,请一定不要忘记!您可以把上述整个结构体类型的声明看作一条语句 struct str { int a; char b; double c; };。
在上述结构体变量 s 中,就可以同时存储三种类型的数据(int/char/double)。什么场景下需要同时存储这么多数据类型呢?我们假设描述一个人,因此就需要声明出一个“‘人’类型”,创建出一个具体的人变量。
// 使用结构体变量描述一个“人类型”, 并且创建一个“人变量”
struct pep
{
char name[10]; // 名字
int age; // 年龄
char sex[5]; // 性别
char id[20]; // 身份证
};有了上述这个结构体类型,我们通过创建一个对应的变量,就可以准确描述一个“人”变量,内部同时存储了一个人的名字、年龄、性别、身份证等信息。这样,后续关于一个人的处理就会更加方便简单。
1.2.初始结构体变量
但是结构体变量我们有了,我们该怎么设置结构体变量的初始值呢(就像 int i = 1 中 i 的初始值为 1 一样)?可以像初始化数组一样初始化一个结构体变量。
// 尝试初始化一个结构体变量
struct pep
{
char name[10]; // 名字
int age; // 年龄
char sex[5]; // 性别
char id[100]; // 身份证
};
int main()
{
struct pep p = { "limou", 13, "女", "4465667655453625" };
return 0;
}甚至可以嵌套初始化,并且这也说明了结构体内部的类型出了基本数据类型,还可以是其他的结构体类型嵌套。
// 尝试初始化一个更加复杂的结构体变量
struct grade
{
int chinese; // 语文成绩
int math; // 数学成绩
};
struct pep
{
char name[10]; // 名字
int age; // 年龄
char sex[5]; // 性别
char id[100]; // 身份证
struct grade a; // 嵌套的结构体变量, 内部存储每一个学科的成绩
};
int main()
{
struct pep p = { "limou", 13, "女", "4465667655453625", {100, 120} };
return 0;
}我们使用 VS2022 的调试功能来检查一下变量 p 内部每个数据成员的值。
-BNDvZxwh.png)
2.结构体的访问
如果此时我们创建并初始化好一个结构体变量,那么我们应该怎么做才能访问其内部的成员进行读取和修改呢?
2.1.采用 . 操作符
可以使用 . 操作符直接访问一个变量内部的成员,您可以对访问到的成员进行读取和修改。
// 使用 . 访问结构体变量的内部成员
struct grade
{
int chinese; // 语文成绩
int math; // 数学成绩
};
struct pep
{
char name[10]; // 名字
int age; // 年龄
char sex[5]; // 性别
char id[100]; // 身份证
struct grade a;
};
int main()
{
struct pep p = { "limou", 13, "女", "4465667655453625", {100, 120} };
printf("名字 %s\n", p.name);
printf("年龄 %d\n", p.age);
printf("性别 %s\n", p.sex);
printf("学号 %s\n", p.id);
printf("语文成绩 %d\n", p.a.chinese);
printf("数学成绩 %d\n", p.a.math);
return 0;
}2.2.采用 -> 操作符
有时候会使用一个结构体指针指向一个结构体对象,但是我们希望使用指针,来间接访问指针指向的结构体变量的内部成员。
// 使用 -> 来访问指针指向的结构体变量内部的成员
#include <stdio.h>
struct grade
{
int chinese; // 语文成绩
int math; // 数学成绩
};
struct pep
{
char name[10]; // 名字
int age; // 年龄
char sex[5]; // 性别
char id[100]; // 身份证
struct grade a;
};
int main()
{
struct pep p = { "limou", 13, "女", "4465667655453625", {100, 120} };
struct pep* pp = &p;
printf("%s\n", pp->name);
printf("%d\n", pp->age);
printf("%s\n", pp->sex);
printf("%s\n", pp->id);
printf("%d\n", pp->a.chinese);
printf("%d\n", pp->a.math);
return 0;
}有什么场景会出现这种需要用到指针的情况呢?在本系列文章中主要有两种情况,一是结构体变量给函数传参,二是在数据结构中的链表。其中第一种我后面就开始讲解,另外一种我将在数据结构中的链表进行展开...
3.结构体的传参
3.1.直接传递结构体变量
结构体传参可以传递整个结构体变量交给函数(这是为了让结构体变量和普通的变量保持同步),不过也一样涉及到实参和形参的问题,这种传递实际上也是一种拷贝,我们可以使用指针来证明一下。
// 把结构体变量直接传递出去
// 声明一个结构体类型
struct S
{
int datas[10];
int num;
};
struct S s = { { 1, 2, 3, 4 } , 10}; // 声明一个全局的结构体变量
// 打印结构体变量的成员
void print(struct S s)
{
printf("%d\n", s.num);
for(int i = 0; i < s.num; i++)
{
printf("%d ", s.datas[i]);
}
}
int main()
{
print(s); // 传递结构体变量
return 0;
}3.2.传递结构体变量指针
但如果结构体的大小比较大,一般会使用指针传参会更好。这是因为会比较省内存,函数在传参的时候,参数是需要压栈的(也就是拷贝进栈区)。而如果传递一个结构体对象过大时,则参数压栈的系统开销会比较大(拷贝的数据比较多),导致程序性能下降。
// 把指向结构体变量的指针传递过去
// 声明一个结构体类型
struct S
{
int datas[10];
int num;
};
struct S s = { { 1, 2, 3, 4 } , 10}; // 声明一个全局的结构体变量
// 打印结构体变量的成员
void print(struct S* s)
{
printf("%d\n", s->num);
for(int i = 0; i < s->num; i++)
{
printf("%d ", s->datas[i]);
}
}
int main()
{
print(&s); // 传递结构体变量
return 0;
}