书中的例子是,在多座岛屿间规划航线,并记录,将岛屿作为节点,数据结构如下
typedef struct island { char *name; char *opens; char *closes; struct island *next; } island;
注意,递归结构(含有指向相同类型的指针)不能为匿名结构,结构中相同类型的指针,在c语法中不允许通过别名来声明它。
创建create,display,release函数
create:
island *create(char *name) { island *i = malloc(sizeof(island)); i->name = strdup(name); i->opens = "09:00"; i->closes = "17:00"; i->next = NULL; return i; }
分配sizeof(island)大小的堆内存,并将地址给i,malloc的返回值是void*类型,可以充当任何类型指针赋值操作的右值,但是有的编译器可能会警告。
给成员赋值:i->name=strdup(name);strdup的含义是在内存中拷贝name,并返回地址,返回值类型为char* ,之所以这么做,是因为name只是用做接收输入的字符串,在内存中的位置固定,且内容会不断更新,如果直接使用name,那么就会有多个节点(i->name)指向同一个地址,结果就是,多个岛屿的名字都是最后一次输入的那个。
i->next=NULL;由于创建岛屿时不知道航线的下一个目的地是哪,所以逻辑上要置空,还有一个原因就是,如果不把指针置为NULL,那么指向的内存位置是不确定的,容易导致意想不到的问题。
display:
void display(island *start) { island *i = start; for (; i != NULL; i = i->next) printf("Name:%s\n open:%s-%s\n", i->name, i->opens, i->closes); }
将链表(航线)的起始指针传入,使用一个island类型的指针进行遍历,这个指针充当的是游标/迭代器。
release:释放内存
void release(island *start) { island *i = start; island *next = NULL; for (; i != NULL; i = next) { next = i->next; free(i->name); free(i); } printf("finish releasing.\n"); }
使用两个island类型指针从起始位置开始进行顺序释放。逻辑步骤:将i指向起始位置,如果i指向的位置的next不为空,next指向起始节点的下一个节点,释放i,将i指向next指向的位置,然后循环。
free()接收一个void*指针,返回值类型为void,注意一点:free(i)之前,要先把i->name释放掉,还记得吗,我们用strdup拷贝的name,如果直接free(i),那么拷贝的这个name的那部分内存,就再也访问不到了,没有人知道他的地址,造成内存泄漏,free函数并没有那么智能,所以我们要手动的将这部分内存释放掉,然后再free(i)。
main函数:
int main() { printf("Game on\n"); island *start = NULL; //i as a iterator island *i = NULL; island *node = NULL; char name[80]; for (; fgets(name, 80, stdin); i = node) { if (name[strlen(name) - 1] == '\n') name[strlen(name) - 1] = 0; //node leads to a piece of storage which contains the new island,the 'node' pointer changes every time you create a new island. node = create(name); if (start == NULL) { start = node; } if (i != NULL) { i->next = node; } } display(start); release(start); return 0; }
创建3个island指针,start代表该链表(航线)的起始位置,i作为迭代器将多个节点串联起来,node是新创建的island,每次创建都会更新node的地址,链表中每个节点只是一段内存(连续或不连续),并没有实际的名称。
逻辑步骤:接收一个island名,创建island,用node指针标记这一新建节点,如果当前start为空,将start赋值为node,如果i不为空,将i->next指向node,最后i=node,将i指向新创建的节点(为连接下一个节点做准备),循环这一步骤。完成这一系列操作后,node,i,i->next似乎都指向同一块内存。
单链表的创建在逻辑上绝对不会有太大出入,一个递归结构作为节点,循环创建节点,用一个临时指针串联所有节点,只start起始指针标记这一链表。
代码已上传到github:https://github.com/AlexTuan1024/egsonhfc.git