# 结构体
# 认识结构体
# 声明结构体
学过面向对象语言的同学可以类比一下,C中的结构体跟对象比较相似。可以使用结构体(Struct)来存放一组不同类型的数据。
结构体是一种集合,它里面包含了多个变量或数组,它们的类型可以相同,也可以不同,每个这样的变量或数组都称为结构体的成员(Member)。
/*声明结构体*/
struct 结构体名{
结构体所包含的变量或数组
};
2
3
4
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在学习小组
float score; //成绩
};
2
3
4
5
6
7
- 结构体成员的定义方式与变量和数组的定义方式相同,只是不能初始化。
- 注意大括号后面的分号;不能少,这是一条完整的语句。
# 结构体变量
// 声明结构体变量 stu1, stu2
struct stu stu1, stu2;
2
也可以在定义结构体的同时定义结构体变量
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在学习小组
float score; //成绩
} stu1, stu2;
2
3
4
5
6
7
如果只需要 stu1、stu2 两个变量,后面不需要再使用结构体名定义其他变量,那么在定义时也可以不给出结构体名
struct{ //没有写 stu
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在学习小组
float score; //成绩
} stu1, stu2;
2
3
4
5
6
7
# 结构体与结构体变量的区别
- 结构体是一种自定义的数据类型,是创建变量的模板,不占用内存空间;
- 结构体变量才包含了实实在在的数据,需要内存空间来存储。
注意
注意在编译器的具体实现中,各个成员之间可能会存在缝隙,对于 stu1、stu2,成员变量 group 和 score 之间就存在 3 个字节的空白填充(见下图)。这样算来,stu1、stu2 其实占用了 17 + 3 = 20 个字节

# 成员的获取与赋值
#include <stdio.h>
int main()
{
/*【声明结构体】*/
struct stu {
char *name;
int age;
float score;
};
/*【声明结构体变量】*/
struct stu stu1, stu2;
/*上面代码等价于*/
/*【声明结构体并同时声明结构体变量】*/
/*
* struct stu {
* char *name;
* ...
* } stu1, stu2;
*/
/*【给结构体成员赋值】*/
stu1.name = "Jack";
stu1.age = 18;
stu1.score = 138.5;
/*可以更精简一点,上面代码等价于*/
/*【声明结构体并同时声明结构体变量并同时给结构体成员赋值】*/
/*
* struct {
* char *name;
* ...
* } stu1, stu2 = { "Jack", 18, 138.5};
*/
/*【读取结构体成员的值】*/
printf("性名:%s\n年龄:%d\n成绩:%f\n", stu1.name, stu1.age, stu1.score);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
注意
上例中整体赋值仅限于定义结构体变量的时候,在使用过程中只能对成员逐一赋值,这和数组的赋值非常类似。
# 结构体数组
结构体数组: 是指数组中的每个元素都是一个结构体。在实际应用中,C语言结构体数组常被用来表示一个拥有相同数据结构的群体,比如一个班的学生、一个车间的职工等。
# 简单定义结构体数组
struct stu{
char *name; //姓名
int age; //年龄
float score; //成绩
}class[5];
// 表示一个班级有5个学生。
2
3
4
5
6
# 定义时同时初始化
结构体数组在定义的同时也可以初始化
struct stu{
char *name; //姓名
int age; //年龄
float score; //成绩
}class[3] = {
{"张三", 18, 145.0},
{"李四", 19, 130.5},
{"王五", 18, 148.5},
};
2
3
4
5
6
7
8
9
# 省略数组长度
当对数组中全部元素赋值时,也可不给出数组长度
struct stu{
char *name; //姓名
int age; //年龄
float score; //成绩
}class[] = {
{"张三", 18, 145.0},
{"李四", 19, 130.5},
{"王五", 18, 148.5},
};
2
3
4
5
6
7
8
9
# 使用结构体数组中元素的成员
- 单独初始化:
class[0].name = "张三"
; - 获取:
int liSiAge = class[1].age
。
# 综合示例
#include <stdio.h>
/*1. 打印班级表格*/
/*2. 计算全班学生的总成绩、平均成绩和以及 140 分以下的人数*/
int main()
{
struct stu {
char* name;
int age;
float score;
} class[] = {
{"张三", 18, 145.0},
{"李四", 19, 130.5},
{"王五", 18, 148.5},
{"陈六", 17, 139.0},
{"张七", 17, 144.5}
};
int len = sizeof(class) / sizeof(struct stu), _140num = 0;
float sum = 0, average = 0;
printf("姓名\t年龄\t成绩\t\n");
for (int i = 0; i < len; i++) {
/*打印班级表格*/
printf("%s\t%d\t%.2f\t\n", class[i].name, class[i].age, class[i].score);
/*计算*/
sum += class[i].score;
if (class[i].score < 140) {
_140num++;
}
}
average = sum / len;
printf("班级总成绩:%.2f\n班级平均成绩:%.2f\n140 分以下的人数:%d\n", sum, average, _140num);
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
打印结果

注意
在做本题时,遇到一个坑,C语言为强类型语言,声明时的数据类型在使用时要用对应的数据类型,比如声明时 float sum;
,在打印时一定要用 %f
(上例中为%.2f
表示单精度浮点型保留两位小数),而不能用 %d
# 结构体指针
当一个指针变量指向结构体时,我们就称它为结构体指针
struct 结构体名 *变量名;
跟定义结构体变量时基本一样。
# 定义一个结构体指针
//结构体
struct stu {
char *name; //姓名
int age; //年龄
float score; //成绩
} stu1 = { "Tom", 18, 136.5 };
// 结构体指针
struct stu *pstu = &stu1;
/*可以简写*/
//结构体
struct stu {
char *name; //姓名
int age; //年龄
float score; //成绩
} stu1 = { "Tom", 18, 136.5 }, *pstu = &stu1;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
注意
- 结构体变量名和数组名不同,数组名在表达式中会被转换为数组指针,而结构体变量名不会,无论在任何表达式中它表示的都是整个集合本身,要想取得结构体变量的地址,必须在前面加
&
; - 万万不可直接取一个结构体名的,也不能将它赋值给其他变量,如下:
struct *pstu = &stu // (X)
// 因为结构体不占内存,只有结构体变量才占内存
struct *pstu = &stu1
2
3
# 获取结构体成员
- 一般形式:
(*pointer).memberName
- 常用形式:
pointer -> memberName
注意
- 第一种形式中,因为
.
的优先级高于*
,所以一定别忘了括号; - 第二种写法中,
->
是一个新的运算符,可以通过结构体指针直接取得结构体成员,这也是->
在C语言中的唯一用途。
# 示例
- 结构体指针的使用
#include <stdio.h>
/*使用结构体指针*/
int main() {
struct stu
{
char *name;
int age;
float score;
} stu1 = { "Tom", 24, 135.5 }, *p = &stu1;
/*使用普通结构体变量成员*/
printf("姓名:%s\n年龄:%d\n成绩:%.2f\n", stu1.name, stu1.age, stu1.score);
/*使用结构体指针访问成员*/
printf("姓名:%s\n年龄:%d\n成绩:%.2f\n", p->name, p->age, p->score);
/*二者打印结果一致*/
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 使用结构体数组指针
#include <stdio.h>
int main()
{
struct str {
char *name;
int age;
float score;
} class[] = {
{"张三", 18, 145.0},
{"李四", 19, 130.5},
{"王五", 18, 148.5},
{"陈六", 17, 139.0},
{"张七", 17, 144.5}
}, *p = class;
int len = sizeof(class) / sizeof(struct str);
for (int i = 0; i < len; i++, p++) {
printf("姓名:%s\t年龄:%d\t成绩:%.1f\t\n", p->name, p->age, p->score);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 结构体指针作为函数参数
结构体变量名代表的是整个集合本身,作为函数参数时传递的整个集合,也就是所有成员,而不是像数组一样被编译器转换成一个指针,最好的办法就是使用结构体指针,这时由实参传向形参的只是一个地址,非常快速
#include <stdio.h>
/*结构体指针作为函数参数*/
struct stu {
char* name;
int age;
float score;
} class[] = {
{"张三", 18, 145.0},
{"李四", 19, 130.5},
{"王五", 18, 148.5},
{"陈六", 17, 139.0},
{"张七", 17, 144.5}
};
/*计算全班学生的总成绩、平均成绩和以及 140 分以下的人数*/
void stuMath(struct stu *p, int len)
{
float sumScore = 0;
int _140num = 0;
for (int i = 0; i < len; i++)
{
sumScore += (p + i)->score;
if ((p + i)->score < 140)
{
_140num++;
}
}
printf("总成绩:%.1f\n平均成绩:%.1f\n140分以下的人数:%d\n", sumScore, sumScore/len, _140num);
}
int main()
{
int len = sizeof(class) / sizeof(struct stu);
stuMath(class, len);
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35