C语言---链表(包括学习过程中的思想活动)
写链表想法的由来:
链表---重要的数据结构。
特简单的:
首先呢!先码一个特别简单的,主要是为了先了解一下嘛!然后在一点点深入嘛!毕竟这么长时间没有写过了,不是嘛?
#include <stdio.h> #include <stdlib.h> struct node { int number; struct node *next; }; typedef struct node Node; int main(void) { Node *head, *b, *c; head = (Node *)malloc(sizeof(Node)); b = (Node *)malloc(sizeof(Node)); c = (Node *)malloc(sizeof(Node)); b->number = 20; c->number = 30; head->next = b; b->next = c; while (head) { printf("---->%d", head->number); head = head->next; } printf("\n"); free(head); free(b); free(c); return 0; }
// 执行结果
[root@yuan misc]# gcc link_list.c
[root@yuan misc]# ./a.out
---->0---->20---->30
链表从表面的意思上就是链状的表,就像一根线上系了很多一样的结,这些结就是我们定义的结构体,而链就是我们定义的结构体中的指针next。next中存放着下一个节点的地址,这样我们就把所有的节点串联了起来,就像我们上边执行的结果一样。其实就是指针将数据串联。
稍微有点复杂的:
(我觉得上边码的那个并不算是真正意义上的链表,我们只是把三个结构体变量串联了起来)写一个稍微复杂点的链表:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 5 struct node { 6 char name[16]; 7 char number[16]; 8 // long long number; 9 struct node *next; 10 }; 11 12 typedef struct node Node; 13 14 Node *createList(int num) 15 { 16 Node *head = NULL; 17 Node *tmp = NULL; 18 Node *node = NULL; 19 int i = 0; 20 // int count = 0; 21 // long long tmpNum; 22 23 head = (Node *)malloc(sizeof(Node)); 24 if (!head) { 25 printf("malloc Node(head) error!\n"); 26 goto err; 27 } 28 29 head->next = NULL; 30 31 tmp = head; 32 33 for(i = 1; i <= num; i++) { 34 printf("create [%d] node!\n", i); 35 node = (Node *)malloc(sizeof(Node)); 36 if (!node) { 37 printf("malloc Node(node) error!\n"); 38 } 39 40 printf("Please enter your name :\n"); 41 scanf("%s", &node->name); 42 if (!node->name) { 43 printf("enter the name error! Please enter again!\n"); 44 goto err; 45 } 46 47 printf("Please enter yout number :\n"); 48 // scanf("%d", &node->number); 49 scanf("%s", &node->number); 50 if (strlen(node->number) != 11) { 51 printf("enter the number error! Please enter again!\n"); 52 goto err; 53 } 54 55 // tmpNum = node->number; 56 // while(tmpNum) { 57 // tmpNum /= 10; 58 // count++; 59 // } 60 61 // if (count != 10) { 62 // printf("enter the number error! Please enter again!\n"); 63 // goto err; 64 // } 65 66 tmp->next = node; 67 tmp = node; 68 } 69 node->next = NULL; 70 return head; 71 72 err: 73 return NULL; 74 } 75 76 void printList(Node *node) 77 { 78 Node *tmp = NULL; 79 80 tmp = node->next; 81 82 while(tmp) { 83 printf("name : %s, number : %s\n", tmp->name, tmp->number); 84 tmp = tmp->next; 85 } 86 } 87 88 void freeList(Node *node) 89 { 90 Node *tmp = NULL; 91 int i = 1; 92 93 tmp = node->next; 94 95 while(tmp) { 96 free(tmp); 97 printf("clean up [%d] node!\n", i); 98 tmp = tmp->next; 99 i++; 100 } 101 tmp = NULL; 102 103 free(node); 104 node = NULL; 105 106 printf("clear finish!\n"); 107 } 108 109 int main(void) 110 { 111 Node *node = NULL; 112 int num; 113 114 printf("Please enter the number of nodes int the link_list :\n"); 115 scanf("%d", &num); 116 node = createList(num); 117 printList(node); 118 freeList(node); 119 120 return 0; 121 }
// 执行结果
[root@localhost address_list]# ./a.out
Please enter the number of nodes int the link_list :
2
create [1] node!
Please enter your name :
test
Please enter your number :
12345678901
create [2] node!
Please enter your name :
test2
Please enter your number :
12345678902
name : test, number : 12345678901
name : test2, number : 12345678902
clean up [1] node!
clean up [2] node!
clear finish!
代码中有部分注掉的,那部分是因为数字长度的问题,我试了%ll,%l64d, %lld,%ld输出的结果都不是期望值,所以就改用了字符串,这个问题有时间我在查一下吧!
逐句分析:
首先我们定义了一个结构体Node,结构体成员有三个,用来存储一个人名和其对应的电话号码(11位),next指针用来指向下一个节点;
在main函数中,先要输入一个数字,来确定我们的链表的长度,然后创建链表,将其存储的内容打印出来,最后释放各个节点。
createList函数中:
生成一个头节点,根据num确定需要生成节点的个数,并对其各个节点进行赋值。
createList函数中我们定义了三个结构体变量,一个是头节点head,一个是节点node,还有一个临时结构体变量tmp。我们将头节点head赋值给中间变量tmp,然后分配一个新的节点node,将node赋值给中间变量tmp结构体成员变量中的next指针变量(也就是头节点head结构体成员变量中的next指针变量),这样头节点就和新分配的节点node串联起来了。其中有一句tmp = node,这句话表示将新生成的节点node赋值给了中间变量tmp,这样做是为了在下一次循环中中间变量tmp表示的是上一次分配的node节点,再次分配新的节点后和现在的中间变量tmp(也就是上一次分配的节点)串联。描述的有点乱,仔细想一下也不是那么乱。
当循环结束后,将最后分配的节点node结构体成员中的next指针变量指向NULL。
返回头节点head。
printList函数中:
刚开始是这样写的:
void printList(Node *node) { while(node) { printf("name : %s, number : %s\n", node->name, node->number); node = node->next; } }
// 执行结果
[root@localhost address_list]# ./a.out
Please enter the number of nodes int the link_list :
2
create [1] node!
Please enter your name :
test
Please enter your number :
12345678901
create [2] node!
Please enter your name :
test2
Please enter your number :
12345678902
name : , number :
name : test, number : 12345678901
name : test2, number : 12345678902
clean up [1] node!
clean up [2] node!
clear finish!
从执行结果中可以看出,我们链表长度是2,但是在打印的出来的结果中去出现三个,只是因为其中空的那个是头节点。
freeList函数:
由于使用了malloc函数动态分配就需要free函数掉,防止内存泄漏,其中有一句tmp = NULL;这句是为了防止野指针,但是NULL也是一个地址,只不过这个地址是确定的(void *)0;但是这句话加到循环里就会出现问题,因为在free掉tmp后这个指针还是存在的,可能就是野指针吧(这里我不确定),但是这时的tmp确确实实是存在的,所以才有了下面的操作。而当我们将tmp制空后,它的地址也就不存在了,我们在进行后边的操作的时候就操作非法地址,就会出现段错误。
还需将头节点free掉。