微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 硬件工程师文库 > 链表结点的数据结构该如何定义

链表结点的数据结构该如何定义

时间:08-20 来源:电子发烧友网工程师 点击:

时需要操作各个结点的p_next指针。而将数据和p_next分离的目的就是使各自的功能职责分离,链表只需要关心p_next的处理,用户只关心数据的处理。因此,对于用户来说,链表结点的定义就是一个"黑盒子",只能通过链表提供的接口访问链表,不应该访问链表结点的具体成员。

为了完成头结点的初始赋值,应该提供一个初始化函数,其本质上就是将头结点中的p_next成员设置为NULL。链表初始化函数原型为:

int slist_init (slist_node_t *p_head); 

由于头结点的类型与其它普通结点的类型一样,因此很容易让用户误以为,这是初始化所有结点的函数。实际上,头结点与普通结点的含义是不一样的,由于只要获取头结点就可以遍历整个链表,因此头结点往往是被链表的拥有者持有,而普通结点仅仅代表单一的一个结点。为了避免用户将头结点和其它结点混淆,需要再定义一个头结点类型(slist.h):

typedef  slist_node_t      slist_head_t; 

基于此,将链表初始化函数原型(slist.h)修改为:

int slist_init (slist_head_t *p_head);

其中,p_head指向待初始化的链表头结点,slist_init()函数的实现详见程序清单3.15。

程序清单3.15  链表初始化函数

1    int slist_init (slist_head_t *p_head) 

2    { 

3        if (p_head == NULL){ 

4                  return -1;

5        }

6        p_head -> p_next = NULL;

7        return 0;

8    }

在向链表添加结点前,需要初始化头结点。即:

slist_node_t head; 

slist_init(&head);

由于重新定义了头结点的类型,因此添加结点的函数原型也应该进行相应的修改。即:

int slist_add_tail (slist_head_t *p_head, slist_node_t *p_node);

其中,p_head指向链表头结点,p_node为新增的结点,slist_add_tail()函数的实现详见程序清单3.16。

程序清单3.16  新增结点范例程序

1    int slist_add_tail (slist_head_t *p_head, slist_node_t *p_node)

2    { 

3             slist_node_t *p_tmp;

4

5         if ((p_head == NULL) || (p_node == NULL)){

6                  return -1;

7             }

8         p_tmp = p_head;

9         while (p_tmp -> p_next != NULL){

10             p_tmp = p_tmp -> p_next;

11      } 

12      p_tmp -> p_next  = p_node; 

13      p_node -> p_next = NULL;

14      return 0;

15  } 

同理,当前链表的遍历采用的还是直接访问结点成员的方式,其核心代码如下:

1    slist_node_t *p_tmp = head.p_next;

2    while (p_tmp != NULL){

3         printf("%d  ", ((slist_int_t *)p_tmp)->data);

4         p_tmp = p_tmp->p_next;

5    }

这里主要对链表作了三个操作:(1)得到第一个用户结点;(2)得到当前结点的下一个结点;(3)判断链表是否结束,与结束标记(NULL)比较。

基于此,将分别提供三个对应的接口来实现这些功能,避免用户直接访问结点成员。它们的函数原型为(slist.h):

slist_node_t *slist_begin_get (slist_head_t *p_head);               // 获取开始位置,第一个用户结点 

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top