P87-P90
https://www.bilibili.com/video/BV18p4y167Md?p=87&vd_source=90f77914d35a141cd8083716ada7726e
有关llist_travel(handler,print_s)遍历链表打印输出的代码:
llist.h
typedef void llist_op(const void *);
void llist_travel(LLIST *,llist_op *);
main.c
static void print_s(const void *record)
{
const struct score_st *r = record;
printf("%d %s %d %d\n",r->id,r->name,r->math,r->chinese);
}
llist_travel(handler,print_s);
llist.c
void llist_travel(LLIST *ptr,llist_op *op)
{
struct llist_node_st *cur;
for(cur = ptr->head.next; cur != &ptr->head; cur = cur->next)
op(cur->data);
}

考虑通用性,llist_travel()在遍历链表之后会遇到问题,不知道*data里面的成员是什么,因为是用户创建的,无法提前定义。
所以需要用户提供一个输出每个成员的函数。将用户提供的函数作为参数,就可以解决这个问题。这就是回调函数。这就得有一个接口,告诉用户回调函数的形式。
首先来看llist.h里的typedef void llist_op(const void *);这行代码,这是接口函数的声明,为什么这么写,考虑到是打印输出,默认不需要返回值,所以形式是void (),那么里面的参数呢?考虑到接口要打印输出各种类型的值,所以参数是指针,因为指针可以指向各种类型的数据,并且指针指向的类型是不确定的,用void。那么形式就是void (void *);然后取一别名,llist_op。
那么用户就可以指定打印输出的数据类型和成员名也就是print_s();函数里面两行。
llist_travel();函数也可以通过回调函数打印输出无法提前定义类型的*data。
理解了这个,后面的find,delete,fetch就容易了。下面是这几节视频的代码。

llist.h
#ifndef LLIST_H__
#define LLIST_H__
#define LLIST_FORWARD 1
#define LLIST_BACKWARD 2
typedef void llist_op(const void *);
typedef int llist_cmp(const void *,const void *);
struct llist_node_st
{
void *data;
struct llist_node_st *prev;
struct llist_node_st *next;
};
typedef struct
{
int size;
struct llist_node_st head;
}LLIST;
LLIST *llist_create(int initsize);
int llist_insert(LLIST *,const void *data,int mode);
void *llist_find(LLIST *,const void *key,llist_cmp *);
int llist_delete(LLIST *,const void *key,llist_cmp *);
int llist_fetch(LLIST *,const void *key,llist_cmp *,void *data);
void llist_travel(LLIST *,llist_op *);
void llist_destroy(LLIST *);
#endif
llist.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "llist.h"
LLIST *llist_create(int initsize)
{
LLIST *new;
new = malloc(sizeof(*new));
if(new == NULL)
return NULL;
new->size = initsize;
new->head.data = NULL;
new->head.prev = &new->head;
new->head.next = &new->head;
return new;
}
int llist_insert(LLIST *ptr,const void *data,int mode)
{
struct llist_node_st *newnode;
newnode = malloc(sizeof(*newnode));
if(newnode == NULL)
return -1;
newnode->data = malloc(ptr->size);
//if(newnode->data == NULL);一个分号找半个小时。
if(newnode->data == NULL)
return -2;
memcpy(newnode->data,data,ptr->size);
if(mode == LLIST_FORWARD)
{
newnode->prev = &ptr->head;
newnode->next = ptr->head.next;
// newnode->prev->next = newnode;
// newnode->next->prev = newnode;
}
else if(mode == LLIST_BACKWARD)
{
newnode->prev = ptr->head.prev;
newnode->next = &ptr->head;
// newnode->prev->next = newnode;
// newnode->next->prev = newnode;
}
else //error
{
return -3;
}
newnode->prev->next = newnode;
newnode->next->prev = newnode;
return 0;
}
static struct llist_node_st * find_(LLIST *ptr,const void *key,llist_cmp *cmp)
{
struct llist_node_st *cur;
for(cur = ptr->head.next; cur != &ptr->head; cur = cur->next)
{
if(cmp(key,cur->data) == 0)
break;
}
return cur;
}
void *llist_find(LLIST *ptr,const void *key,llist_cmp *cmp)
{
return find_(ptr,key,cmp)->data;
}
int llist_delete(LLIST *ptr,const void *key,llist_cmp *cmp)
{
struct llist_node_st *node;
node = find_(ptr,key,cmp);
if(node == &ptr->head)
return -1;
node->prev->next = node->next;
node->next->prev = node->prev;
free(node->data);
free(node);
return 0;
}
int llist_fetch(LLIST *ptr,const void *key,llist_cmp *cmp,void *data)
{
struct llist_node_st *node;
node = find_(ptr,key,cmp);
if(node == &ptr->head)
return -1;
node->prev->next = node->next;
node->next->prev = node->prev;
if(data != NULL)
memcpy(data,node->data,ptr->size);
free(node->data);
free(node);
return 0;
}
void llist_travel(LLIST *ptr,llist_op *op)
{
struct llist_node_st *cur;
for(cur = ptr->head.next; cur != &ptr->head; cur = cur->next)
op(cur->data);
}
void llist_destroy(LLIST *ptr)
{
struct llist_node_st *cur,*next;
for(cur = ptr->head.next; cur != &ptr->head; cur = next)
{
next = cur->next;
free(cur->data);
free(cur);
}
free(ptr);
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "llist.h"
#define NAMESIZE 32
struct score_st
{
int id;
char name[NAMESIZE];
int math;
int chinese;
};
static void print_s(const void *record)
{
const struct score_st *r = record;
printf("%d %s %d %d\n",r->id,r->name,r->math,r->chinese);
}
static int id_cmp(const void *key,const void *record)
{
const int *k = key;
const struct score_st *r = record;
return (*k - r->id);
}
static int name_cmp(const void *key,const void *record)
{
const char *k = key;
const struct score_st *r = record;
return strcmp(k,r->name);
}
int main()
{
LLIST *handler;
struct score_st tmp;
int ret,i;
int id = 3;
char *del_name = "std6";
struct score_st *data;
handler = llist_create(sizeof(struct score_st));
if(handler == NULL)
exit(1);
for(i = 0; i < 7; i++)
{
tmp.id = i;
snprintf(tmp.name,NAMESIZE,"std%d",i);
tmp.math = rand()%100;
tmp.chinese = rand()%100;
ret = llist_insert(handler,&tmp,LLIST_FORWARD);
if(ret)
exit(1);
}
llist_travel(handler,print_s);
printf("\n\n");
ret = llist_delete(handler,&id,id_cmp);
if(ret)
printf("llist_delete failed!\n");
llist_travel(handler,print_s);
printf("\n\n");
ret = llist_delete(handler,del_name,name_cmp);//compile
if(ret)
printf("llist_delete failed!\n");
llist_travel(handler,print_s);
#if 0
data = llist_find(handler,&id,id_cmp);
if(data == NULL)
printf("Can not find!\n");
else
print_s(data);
#endif
llist_destroy(handler);
exit(0);
}