链表是一种链式的数据结构,可以将结构体链接起来。实现方式是在结构体中定义一个指针,用来指向下一个结构体变量,形成一种链式的结构:
struct film
{
char title[TSIZE];
int rating;
struct film *next;
};
试想如果定义了srtuct film film1,film2; 并且对成员变量title,rating进行初始化,next初始化为NULL,那么film1和film2并没有联系,但是如果film1.next = film2,就可以通过访问film1.next.title访问film2里的title成员。所以定义film类型的next指针可以帮助我们将没有关联的结构体关联起来,这样的一种结构成为链表,我们只需要知道最前面的地址,就可以通过next访问链表中的每一个成员:
current = film1;
while (current != NULL)
{
printf("title: %s rating: %d\n", current.title, current.rating);
current = current.next;
}
如果 srtuct film *film1; 访问时就用->,如current->title, current = current->next;
下面看个具体的例子,用户不确定地添加数据,用于记录影片的片名和评分(0-10),当输入的影片名为空行结束输入,并输出,我们可以定义结构体数组struct film movies[500]然后从movies[0]开始设置,但是数组的地址是连续的,使用链表的话可以让变量地址不连续,实现方式如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TSIZE 45 // 存储片名的数组大小
#define FMAX 2 // 影片最大数量
struct film
{
char title[TSIZE];
int rating;
struct film *next;
};
char *s_gets(char *st, int n)
{
char *ret_val;
char *find;
ret_val = fgets(st, n, stdin);
// printf("%s\n", ret_val);
if (ret_val)
{
find = strchr(st, '\n');
if (find)
{
*find = '\0';
}
else
{
while (getchar() != '\n')
{
continue;
}
}
}
return ret_val;
}
int main()
{
struct film *head = NULL;
struct film *prev, *current; // 这里的赋值都是地址传递,也就是深复制。修改原始数据的next,等号赋值的next也会同步修改
char input[TSIZE];
puts("Enter first movie title:");
while (s_gets(input, TSIZE) != NULL && input[0] != '\0')
{
current = (struct film *)malloc(sizeof(struct film));
if (head == NULL) {
head = current;
} else {
prev->next = current;
// 当head不为NULL时,第二次设置prev->next的值,head->next也会指向current。
// 第三次设置时,prev保留了第二次的current,而head->next此时也指向上一次即第二次设置的current,此时执行prev->next=current,相当于head->next->next=current
}
current->next = NULL;
strcpy(current->title, input);
puts("Enter your rating <0-10>:");
scanf("%d", ¤t->rating);
while (getchar() != '\n')
{
continue;
}
puts("Enter next movie title(empty line to stop):");
prev = current; // 保留上一个节点(也是head最后一个节点),第一次执行时,head和prev都会指向current
}
current = head;
while (current != NULL)
{
printf("Movie title: %s rating: %d\n", current->title, current->rating);
current = current->next;
}
// current = head;
// while (current != NULL)
// {
// current = head;
// head = current->next;
// free(current);
// }
puts("end");
return 0;
}
s_gets函数时对fgets进行了封装,帮助我们按行输入的内容能够正确的赋值给字符串变量,可参考
C语言使用getchar方法清除输入缓冲区和fgets函数的使用_getchar清除缓冲区_海宝162的博客-优快云博客
程序结束时,一般会自动释放malloc分配的内存,也可以用free自己释放,输入结果:
所以本文已经给出了链表基本操作的访问和初始化这两个函数的思路。
在学习链表时,我们需要注意很多时候定义的都是指针变量或者非基本类型,他们相互用等号赋值时,操作的成员变量都是共享的,如a=b;然后将b里面成员title赋值字符串"abc",此时访问a的title成员也是"abc"。因为这里的b赋值的是地址。所以在使用链表时,操作head = current和prev = current,当执行prev->next = current2时,head->next也指向current2。