【链表1】指针和结构体
通过前面8章的讲解,基本上阐述了C语言的基本用法和语法。掌握了这些内容,再通过一些练习,基本上可以满足大家平时的一些简单编程需求了。但是仅仅掌握这些基础的应用还是不够的,C语言的精髓还是它的指针,因此这一章会围绕C语言的指针进行更深一层次的探讨。希望通过本章的学习,可以使读者对C语言的指针有一个更深层次的理解。
指针和结构体
前面我们讲述指针的时候,已经向大家介绍过使用指针去指向普通变量,指向函数,指向数组,指向字符串等操作,由于当时还没有介绍结构体,因此是没有讲述指针指向结构体的。但是,指针作为C语言中最具有标志性的特征,指向结构体也是必定支持的,指向结构体的指针就是本小节要讨论的结构体指针。
在讨论结构体指针之前,我们先来定义一个结构体。定义这个结构体的步骤为,先用“typedef”关键词声明一个结构体的存储类型。
typedef struct
{
char name[20];
int age;
int id_num;
} student_t;
接着,使用这个结构体类型去定义一个结构体变量,“student_t Xiaoming;”。
当这个描述小明信息的结构体变量被定义好之后,实际上编译器会在内存上面开辟出一块内存区域来存储这个结构体变量,如图1所示。
图1 结构体内存排放
当这个结构体内存被定义好之后,我们可以使用“Xiaoming.结构体成员名”的形式来访问这个结构体内存的任意一个成员。那么这个结构体变量的名称具体代表的是什么呢?我们可以使用printf函数将这个结构体变量名和内部各个成员变量的地址打印出来,如图2所示。很显然,结构体变量名的地址和结构体中第一个成员变量的首地址是保持一致的,因此我们可以推理出,结构体变量的名称的地址就是代表着结构体首地址。
图2 结构体变量名的地址和成员变量的地址
基于上面讨论的内容,我们可以尝试一下去定义一个指针,指向这个结构体。但是这个指针究竟应该定义什么类型的呢?前面我们讲指针基础的时候就说过,指向某个变量的指针,其数据类型应该和其指向的内容保持一致。那么指向这个结构体的指针难道我们要使用struct类型的指针,如“struct *pt”?
很显然,这是错误的,因为我们之前讨论过,为什么指向某个变量的指针,其数据类型应该和其指向的内容保持一致。其根本原因是因为要使得指针的增长和其内存保持一致的变化。因此,如果定义“struct *pt”这样的指针,肯定是毫无意义的,语法上也肯定是不对的。
我们前面讲结构体的时候也说过,struct关键词用来申明结构体的时候,归根到底声明的其实是这个结构体的存储类型,因此对于这个结构体,我们已经用typedef将其声明为一个新的数据类型了,所以要定义的这个指针应该是“student_t *stu_pt;”。
现在指向这个结构体的指针已经被定义好了,那么接下来的内容就应该是如何指向这个结构体。按照我们之前的经验,指向操作其实就是将指向对象的地址赋值给一个指针变量,因此这个指向操作就非常简单了,“stu_pt = &Xiaoming”。如图3所示。
图3 定义指向结构体的指针
从图3中我们可以看到,此时指针的内容和其指向的结构体的首地址是一样的,因此我们可以初步判断这个结构体指向操作是正确的。
现在我们已经了解了如何去定义一个结构体指针,以及如何去利用这个结构体指针去指向一个结构体,那么现在问题来了,我们定义结构体的初衷是为了是某几个相关的变量有序地排列在一起,并且对这个结构体变量中的每一个成员都能引用,并且进行读写,而现在,我们定义好了一个结构体指针并且指向了一个结构体变量,那么我们能否使用这个结构体指针去引用其指向的那个结构体里面的成员变量呢?如果可以,那么该如何引用?
在回答这个问题之前,我们来回顾下,一个结构体变量其引用指针的时候,是使用“结构体变量名.成员变量名”的形式。那么对于一个结构体指针来说,这种结构体变量成员名称的引用方式是不被允许的。对于一个指向结构体变量的结构体指针来说,我们应该使用指向符号“->”来引用这个结构体指针指向的结构体变量的成员变量。这一点一定要切记。
如,我们可以利用“student_t”类型定义一个结构体变量Xiaoming,接着再去使用一个结构体指针去指向这个变量。我们先对Xiaoming这个结构体变量里面的成员变量name赋值,接着使用结构体指针去读出。然后再使用结构体指针对age变量赋值,接着再使用结构体变量去读出,如图4所示。
图4 结构体指针访问结构体变量