前面我们学习了数组。
数组可以把多个相同类型的数据放在一起:
int a[5] = {10, 20, 30, 40, 50};但是现实中的一个对象,通常不只有一种数据。
例如一个学生:
姓名 -> 字符串
年龄 -> int
成绩 -> double
如果使用普通变量:
char name[50] = "Alice";
int age = 18;
double score = 95.5;当学生越来越多时,就会变得很麻烦。
结构体就是用来解决这个问题的。
一、什么是结构体
结构体(struct)可以把不同类型的数据组合在一起。
例如:
struct Student {
char name[50];
int age;
double score;
};这里定义了一个 Student 结构体。
可以理解为:
Student
├─ name
├─ age
└─ score
其中:
char name[50];
int age;
double score;叫做结构体的成员。
注意:
上面的代码只是定义了一种结构体类型。
还没有创建真正的变量。
二、struct 定义
基本格式:
struct 结构体名字 {
数据类型 成员1;
数据类型 成员2;
数据类型 成员3;
};例如:
struct Student {
char name[50];
int age;
double score;
};注意最后一定要有:
};也就是结构体定义后需要一个分号。
三、结构体变量
定义结构体之后,就可以创建变量。
例如:
struct Student s;这里:
struct Student
是类型。
s
是变量。
完整代码:
#include <stdio.h>
struct Student {
char name[50];
int age;
double score;
};
int main() {
struct Student s;
return 0;
}四、成员访问 .
结构体变量可以使用:
.
访问成员。
例如:
struct Student s;
s.age = 18;
s.score = 95.5;访问方式:
变量.成员例如:
s.age
s.score修改结构体成员
#include <stdio.h>
struct Student {
char name[50];
int age;
double score;
};
int main() {
struct Student s;
s.age = 18;
s.score = 95.5;
printf("age = %d\n", s.age);
printf("score = %.2f\n", s.score);
return 0;
}运行:
age = 18
score = 95.50
五、结构体初始化
结构体可以在创建的时候直接初始化。
例如:
struct Student s = {"Alice", 18, 95.5};对应关系:
name -> "Alice"
age -> 18
score -> 95.5
完整代码:
#include <stdio.h>
struct Student {
char name[50];
int age;
double score;
};
int main() {
struct Student s = {"Alice", 18, 95.5};
printf("name = %s\n", s.name);
printf("age = %d\n", s.age);
printf("score = %.2f\n", s.score);
return 0;
}六、指定成员初始化
还可以指定初始化某个成员:
struct Student s = {
.name = "Alice",
.age = 18,
.score = 95.5
};这样代码更加清楚。
例如:
struct Student s = {
.age = 18,
.score = 95.5
};没有初始化的成员会使用对应类型的零值。
七、结构体数组
既然数组可以保存多个相同类型的变量,那么当然也可以创建:
结构体数组。
例如:
struct Student students[3];表示:
students[0]
students[1]
students[2]
每一个元素都是:
struct Student初始化结构体数组
struct Student students[3] = {
{"Alice", 18, 95.5},
{"Bob", 19, 88.0},
{"Charlie", 18, 92.5}
};遍历:
#include <stdio.h>
struct Student {
char name[50];
int age;
double score;
};
int main() {
struct Student students[3] = {
{"Alice", 18, 95.5},
{"Bob", 19, 88.0},
{"Charlie", 18, 92.5}
};
for (int i = 0; i < 3; i++) {
printf(
"%s %d %.2f\n",
students[i].name,
students[i].age,
students[i].score
);
}
return 0;
}这里结合了之前学过的:
数组
+
for
+
结构体
八、结构体指针
结构体当然也可以使用指针。
例如:
struct Student s = {"Alice", 18, 95.5};
struct Student *p = &s;这里:
s
↓
Student 变量
&s
↓
s 的地址
p
↓
保存 s 的地址
所以:
p是一个:
struct Student *类型的指针。
九、通过结构体指针访问成员
如果使用普通结构体变量:
s.age如果使用结构体指针:
p->age这里出现了新的运算符:
->
例如:
#include <stdio.h>
struct Student {
char name[50];
int age;
double score;
};
int main() {
struct Student s = {"Alice", 18, 95.5};
struct Student *p = &s;
printf("%s\n", p->name);
printf("%d\n", p->age);
printf("%.2f\n", p->score);
return 0;
}十、. 和 ->
的区别
这是结构体非常重要的知识。
结构体变量
使用:
.例如:
s.age结构体指针
使用:
->例如:
p->age可以简单记成:
结构体变量
↓
.
结构体指针
↓
->
实际上:
p->age等价于:
(*p).age也就是说:
p->age可以理解为:
(*p).age十一、为什么 (*p).age
要加括号
不能写:
*p.age因为运算符优先级会导致它被理解成:
*(p.age)而 p 是指针,并没有 age 这个成员。
所以应该写:
(*p).age而:
p->age就是专门为结构体指针设计的简写。
十二、结构体作为函数参数
结构体可以传给函数。
例如:
void print_student(struct Student s) {
printf("%s\n", s.name);
printf("%d\n", s.age);
printf("%.2f\n", s.score);
}完整代码:
#include <stdio.h>
struct Student {
char name[50];
int age;
double score;
};
void print_student(struct Student s) {
printf("name: %s\n", s.name);
printf("age: %d\n", s.age);
printf("score: %.2f\n", s.score);
}
int main() {
struct Student s = {"Alice", 18, 95.5};
print_student(s);
return 0;
}这里和普通变量传参类似:
print_student(s);函数收到的是结构体的一份副本。
十三、结构体指针作为函数参数
如果结构体比较大,复制整个结构体可能没有必要。
可以直接传地址。
例如:
void print_student(struct Student *p) {
printf("%s\n", p->name);
printf("%d\n", p->age);
printf("%.2f\n", p->score);
}调用:
print_student(&s);完整代码:
#include <stdio.h>
struct Student {
char name[50];
int age;
double score;
};
void print_student(struct Student *p) {
printf("name: %s\n", p->name);
printf("age: %d\n", p->age);
printf("score: %.2f\n", p->score);
}
int main() {
struct Student s = {"Alice", 18, 95.5};
print_student(&s);
return 0;
}十四、通过函数修改结构体
这一点非常重要。
如果直接传结构体:
void change(struct Student s)修改的是副本。
例如:
void change(struct Student s) {
s.age = 20;
}调用:
change(s);原来的:
s.age不会发生变化。
但是如果传指针:
void change(struct Student *p) {
p->age = 20;
}然后:
change(&s);原来的 s 就会被修改。
完整代码:
#include <stdio.h>
struct Student {
char name[50];
int age;
double score;
};
void change_age(struct Student *p) {
p->age = 20;
}
int main() {
struct Student s = {"Alice", 18, 95.5};
printf("修改前: %d\n", s.age);
change_age(&s);
printf("修改后: %d\n", s.age);
return 0;
}输出:
修改前: 18
修改后: 20
这和你之前学习的:
void swap(int *a, int *b)本质上是同一种思想:
把地址传进去
↓
函数通过地址
↓
直接修改原来的数据
十五、结构体作为函数返回值
函数也可以返回一个结构体。
例如:
struct Student create_student() {
struct Student s = {"Alice", 18, 95.5};
return s;
}然后:
struct Student s = create_student();完整代码:
#include <stdio.h>
struct Student {
char name[50];
int age;
double score;
};
struct Student create_student() {
struct Student s = {"Alice", 18, 95.5};
return s;
}
int main() {
struct Student s = create_student();
printf("%s\n", s.name);
printf("%d\n", s.age);
printf("%.2f\n", s.score);
return 0;
}十六、typedef
每次写:
struct Student稍微有一点麻烦。
可以使用 typedef 给结构体类型起一个别名。
例如:
typedef struct {
char name[50];
int age;
double score;
} Student;以后就可以直接写:
Student s;而不用:
struct Student s;typedef 的基本格式
typedef 原来的类型 新名字;例如:
typedef int Integer;那么:
Integer a = 10;实际上就是:
int a = 10;十七、typedef + struct
推荐掌握这种写法:
typedef struct {
char name[50];
int age;
double score;
} Student;使用:
Student s;完整代码:
#include <stdio.h>
typedef struct {
char name[50];
int age;
double score;
} Student;
int main() {
Student s = {"Alice", 18, 95.5};
printf("%s\n", s.name);
printf("%d\n", s.age);
printf("%.2f\n", s.score);
return 0;
}十八、结构体综合示例
把目前学到的内容组合起来:
#include <stdio.h>
typedef struct {
char name[50];
int age;
double score;
} Student;
void print_student(Student *p) {
printf("姓名: %s\n", p->name);
printf("年龄: %d\n", p->age);
printf("成绩: %.2f\n", p->score);
}
void add_score(Student *p, double score) {
p->score += score;
}
int main() {
Student students[3] = {
{"Alice", 18, 95.5},
{"Bob", 19, 88.0},
{"Charlie", 18, 92.5}
};
add_score(&students[0], 2.0);
for (int i = 0; i < 3; i++) {
print_student(&students[i]);
printf("\n");
}
return 0;
}这里已经同时使用了:
typedef
struct
数组
函数
指针
.
->
for
这就是结构体真正的意义:
把多个有关联的数据组织成一个完整的对象。
十九、结构体在内存中的表现
例如:
struct Student {
int age;
double score;
};创建:
struct Student s;可以粗略理解成:
s
┌──────────────┐
│ age │
├──────────────┤
│ score │
└──────────────┘
而:
&s就是整个结构体的地址。
如果:
struct Student *p = &s;那么:
p
↓
┌──────────────┐
│ age │
├──────────────┤
│ score │
└──────────────┘
因此:
p->age就是通过指针访问结构体内部的 age。
二十、结构体与数组的区别
数组:
int a[3];只能保存:
int
int
int
结构体:
struct Student {
char name[50];
int age;
double score;
};可以保存:
char[]
int
double
所以:
数组
↓
多个相同类型的数据
结构体
↓
多个不同类型、但逻辑相关的数据
两者也可以结合:
Student students[100];表示:
100 个 Student。
二十一、结构体与指针的关系
目前已经可以把这几个东西联系起来:
结构体
↓
Student s
指针
↓
Student *p = &s
普通成员访问
↓
s.age
指针成员访问
↓
p->age
而:
p->age本质上就是:
(*p).age二十二、练习
练习 1
定义一个 Person 结构体:
姓名
年龄
身高
然后创建一个变量并输出。
练习 2
定义:
struct Student包含:
name
age
score
创建一个包含 5 个学生的结构体数组。
使用 for 输出所有学生。
练习 3
编写:
void print_student(Student *p)输出学生的信息。
要求使用:
->访问成员。
练习 4
编写:
void add_score(Student *p, double score)让函数能够修改学生成绩。
练习 5
编写:
Student create_student(...)让函数创建并返回一个学生结构体。
练习 6
定义一个:
typedef struct把结构体类型简化成:
Student然后使用:
Student s;创建变量。
Comments NOTHING