第七章 结构体
第1节
在程序编写中,我们难免会遇到一些复杂的数据类型无法用单一的普通数据类型表示,于是我们便可以通过使用结构体来定义属于我们自己的数据类型。所谓结构体是一些值的集合,这些值成为成员变量。结构体的每个成员可以是不同类型的变量。我们将一个个对象进行抽象,提取其中最关键的最核心的信息,加以表示,并通过结构体的方式表现出来,这样的模式,有点类似于CPP的面向对象的编程,但在CPP中对象和类的概念还要更为强大,更为广泛。
结构体的声明
例如我们要描述一个学生,我们可以将学生的基本信息声明为一个结构体,有了学生类型的结构体我们想要定义学生类型的变量则易如反掌了。1
2
3
4
5struct Student
{
char name[1024];//姓名
int id;//学号
};
通过以上的代码,我们则定义好了一个名为Student
,有两个成员变量name
和id
的结构体,但是由于在C语言中单纯使用结构体定义变量要加上struct
关键字例如struct Student stu1;
,十分麻烦,因此,我们往往用typedef
关键字将其进行重命名来简化使用,例如以下的代码段。1
2
3
4
5typedef struct _student
{
char name[1024];//姓名
int id;//学号
}Student;
有了这样的声明,我们则可以更简单的定义结构体变量,例如Student stu1;
。
结构体变量的定义及初始化
定义
结构体变量的定义主要分为两种方法,一种是在声明时进行定义,另一种是在使用时进行定义。我们先来学习如何在声明时进行定义。1
2
3
4
5struct student
{
char name[1024];//姓名
int id;//学号
}stu1, stu2;
使用以上的方法我们则在声明结构体的同时定义了两个个名为stu1
和stu2
类型为Student
的变量。但是要奇迹这种方法如果在使用typedef
关键字的情况下无法使用。
接下来我们在使用时再进行变量的定义。1
2
3
4
5
6
7
8
9
10
11
12
13#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//添加头文件
#include <stdlib.h>
typedef struct student
{
char name[1024];//姓名
int id;//学号
}Student;
int main()//主函数,函数入口
{
Student stu1, stu2;
}
可以看出使用结构体定义变量的方法和使用普通数据类型定义变量的方法别无二致。在此我依然提倡大家使用typedef
关键字对结构体进行重命名,这样使得我们使用起来更为方便,不过这种习惯在CPP中就可以摒弃了,因为在CPP中哪怕不使用 typedef
关键字,结构体类型定义变量时也不需要加struct
关键字。
初始化
在定义完结构体变量后我们就可以对结构体变量进行初始化,同样的,分为在声明时进行初始化和在使用时进行初始化。我们先学习第一种情况。1
2
3
4
5struct student
{
char name[1024];//姓名
int id;//学号
}stu1 = { "Misaki", 111 }, stu2 = {"张三", 112};
这种初始的方式是和在声明时定义变量的方式一起连用的,达到可以快速定义变量并初始化的效果。这种初始化的语法与数组初始化的语法十分相似,我们都要按照结构体内成员变量的顺序依次初始化,
接下来是在使用时定义变量的情况。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//添加头文件
#include <stdlib.h>
typedef struct student
{
char name[1024];//姓名
int id;//学号
}Student;
int main()//主函数,函数入口
{
Student stu1 = { "Misaki", 111 };
Student stu2 = { "张三", 112 };
system("pause");
}
结构体初始化的方式大同小异,都与数组的初始化十分相似,大家在实际使用中,视情况进行使用。
结构体成员的访问
结构体变量访问成员
在结构体的使用过程中,最常使用的就是访问一个结构体变量的成员变量,不过好在C语言中结构体成员的访问十分的方便和快捷。我们只需要使用一个我们之前提到过的访问结构体成员变量操作符.
即可。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//添加头文件
#include <stdlib.h>
typedef struct student
{
char name[1024];//姓名
int id;//学号
}Student;
int main()//主函数,函数入口
{
Student stu1 = { "Misaki", 111 };
Student stu2 = { "张三", 112 };
printf("stu1的姓名为:%s,学号为:%d\n", stu1.name, stu1.id);
printf("stu2的姓名为:%s,学号为:%d\n", stu2.name, stu2.id);
system("pause");
}
结构体的成员变量也不过是一个普通数据类型的变量,使用起来与普通数据类型别无二致,前提是我们要将其从结构体变量中访问到。从代码中大家可以看出我使用结构体变量.成员变量名
的方式即可轻易访问到一个结构体变量的成员变量。
结构体指针访问成员变量
结构体指针访问结构体成员变量的方法与结构体变量访问成员变量的方法类似,只不过我们要将其中的.
换为->
。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//添加头文件
#include <stdlib.h>
typedef struct student
{
char name[1024];//姓名
int id;//学号
}Student;
int main()//主函数,函数入口
{
Student stu1 = { "Misaki", 111 };
Student* stu2 = &stu1;
printf("stu1的姓名为:%s,学号为:%d\n", stu2->name, stu2->id);
system("pause");
}
结构体嵌套
在结构体使用的过程中,我们声明一个结构体的成员变量为另外一个结构体类型,这样来达到多重嵌套表达复杂数据类型的效果。以下我将展示一个结构体多重嵌套的例子。1
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#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//添加头文件
#include <stdlib.h>
#include <string.h>
typedef struct student
{
char name[50];//姓名
int id;//学号
}Student;
typedef struct area
{
Student students[10];
}Area;
typedef struct city
{
Area areas[10];
}City;
typedef struct province
{
City cities[10];
}Province;
typedef struct country
{
Province provinces[10];
}Country;
int main()//主函数,函数入口
{
Country country;
Country* pCountry = &country;
country.provinces[0].cities[0].areas[0].students[0].id = 111;
strcpy(pCountry->provinces[0].cities[0].areas[0].students[0].name, "Misaki");
printf("学生的学号为:%d,姓名为:%s\n", country.provinces[0].cities[0].areas[0].students[0].id,country.provinces[0].cities[0].areas[0].students[0].name);
system("pause");
}
大家在使用结构体嵌套来表示数据类型时务必要注意开辟合适的内寸空间,在上面的例子中我们每一个嵌套中的结构体都有着10倍的Student
类型的变量的大小,综合起来已经开辟了十分多的内寸空间,如果我们此时将Student
中的大小改为1024大家就会发现程序已经闪退了,这就是我们过度占用内存系统为了保护自身所作出的反应机制。
结构体传参
结构体变量的参数传递与普通变量几乎完全一样,因此我们依旧分为两种方式进行讲解。
形参传递
与普通参数传递一样,这种只传入形参的方法不会对原本结构体变量的内容进行改变。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//添加头文件
#include <stdlib.h>
#include <string.h>
typedef struct student
{
char name[50];//姓名
int id;//学号
}Student;
void Func(Student stu)
{
stu.id = 110;
printf("在函数内部学生的学号为:%d\n", stu.id);
}
int main()//主函数,函数入口
{
Student stu = { "Misaki", 111 };
Func(stu);
printf("在函数外部学生的学号为:%d\n", stu.id);
system("pause");
}
指针传递
传入结构体指针来进行结构体变量的传递使得结构体在过大的情况下更为方便,省去了编译器将变量重新复制一份的时间,更节省空间和时间,是我最为推荐的传递方式。即使我们并不想函数随意更改我们的结构体变量,我们也只需要传递指向常量的结构体指针即可,这样的指针如何定义我在指针章节已经详细讲过。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>//添加头文件
#include <stdlib.h>
#include <string.h>
typedef struct student
{
char name[50];//姓名
int id;//学号
}Student;
void Func(Student* stu)//如果需要给一个指向常量的指针则需要改为 const Student* stu;
{
stu->id = 110;
printf("在函数内部学生的学号为:%d\n", stu->id);
}
int main()//主函数,函数入口
{
Student stu = { "Misaki", 111 };
Func(&stu);
printf("在函数外部学生的学号为:%d\n", stu.id);
system("pause");
}
以上则为结构体章节的全部内容,结构体在大型程序的编写中十分重要,由于C语言中没有类和对象的概念,因此我们只能依靠力量单薄的结构体来进行大型程序中数据类型的管理来达到类似于面向对象编程的效果,不过这样远远不够,因此想要更好的理解“抽象”“泛型”“多态”则需要进行对CPP的学习。
到此C语言初阶部分章节全部结束,本教程目前仅仅教授了C语言最为基本的语法部分,C语言更为深层的使用我们在C语言进阶进行详细讲解。