Spiceserver中链表的使用

来源:互联网 发布:矩阵uv分解 编辑:程序博客网 时间:2024/06/10 06:25

    spiceserver代码中很多地方都用到了链表,刚开始看代码的时候会因为这些链表,或者有关这些链表的宏而感到困惑,造成阅读上的困难,其实这些代码我们可以单独将其提取出来,写一些demo进行验证和测试,这样就能很快对这些链表机制进行熟悉,我们可以用windows上的vs或者linux上的gcc进行编译,本次我将使用VS2013来开发。

 常规链表

    我们在学C语言的时候会学到链表,比如我们要写一个管理学生成绩的链表,我们就可以把数据结构定义成:

typedef struct Student{char szName[16];/* 姓名 */char szNo[16];/* 学号 */int  nScore;/* 成绩 */Student *next;}Student;
    如果我们要管理张三、李四、王五、赵六四个学生的成绩,我们可以在初始化的时候直接把这个链表建好,实现如下:

void AppendList(Student *pHead, const char *szName, const char *szNo, int nScore){Student *pStudent = pHead;while (pStudent != NULL && pStudent->next != NULL){pStudent = pStudent->next;}/* 增加节点 */Student *pItem = (Student *)calloc(1, sizeof(Student));if (pItem == NULL){return;}strcpy_s(pItem->szName, szName);strcpy_s(pItem->szNo, szNo);pItem->nScore = nScore;pStudent->next = pItem;pItem->next = NULL;}Student *InitList(){Student *pHead = (Student *)calloc(1, sizeof(Student));/* 链表头 */if (pHead == NULL){return NULL;}AppendList(pHead, "张三", "0001", 90);AppendList(pHead, "李四", "0002", 88);AppendList(pHead, "王五", "0003", 92);AppendList(pHead, "赵六", "0004", 87);return pHead;}
  我们要遍历打印所有学生信息,实现为:

void PrintList(Student *pHead){Student *pStudent = pHead->next;while (pStudent != NULL){printf("name:%s No.:%s score:%d\n", pStudent->szName, pStudent->szNo, pStudent->nScore);pStudent = pStudent->next;}}
    最后是清理链表,释放内存,实现如下:

void DestroyList(Student *pHead){Student *pStudent = pHead;Student *pTemp;while (pStudent != NULL){pTemp = pStudent;pStudent = pStudent->next;free(pTemp);}}
    我们在主函数调用一般是这样的:

int _tmain(int argc, _TCHAR* argv[]){Student *pHead = InitList();if (pHead){PrintList(pHead);DestroyList(pHead);}return 0;}
    这是一个比较常规的链表,我这里写的比较简单,还有诸如插入、移除等操作我没有写,因为这不是我讲的重点。

Spiceserver链表

    Spiceserver中的链表不是采用以上的方式,如果我用它提供的链表来实现上述需求,那么Student的结构就应该定义为:

typedef struct Student{char szName[16];/* 姓名 */char szNo[16];/* 学号 */int  nScore;/* 成绩 */Ring  item;}Student;

    我们看到了节点的连接是通过Ring这个结构体,它的定义很简单,在Spiceserver源码中有一个Ring.h的文件,我们可以把这个文件拷到我们的工程中,删除掉一些影响我们编译的东西,如spice_assert之类的,对功能不会造成什么影响,Ring的定义如下:
typedef struct Ring RingItem;typedef struct Ring {    RingItem *prev;    RingItem *next;} Ring;
    我把上述代码用这种方式重新写一遍:
// Containerof.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include <stdlib.h>#include <string.h>#include "ring.h"#define SPICE_OFFSETOF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)  #define SPICE_CONTAINEROF(ptr, type, member)  (type *)( (char *)ptr - SPICE_OFFSETOF(type,member) )typedef struct Student{char szName[16];/* 姓名 */char szNo[16];/* 学号 */int  nScore;/* 成绩 */Ring  item;}Student;void AppendList(Student *pHead, const char *szName, const char *szNo, int nScore){Student *pItem = (Student *)calloc(1, sizeof(Student));if (pItem == NULL){return;}strcpy_s(pItem->szName, szName);strcpy_s(pItem->szNo, szNo);pItem->nScore = nScore;ring_item_init(&pItem->item);if (ring_is_empty(&pHead->item)){/* 如果是空链表,直接加在后面,因为ring_get_tail会返回NULL */ring_add(&pHead->item, &pItem->item);}else{Ring *pTailItem = ring_get_tail(&pHead->item);ring_add(pTailItem, &pItem->item);}}Student *InitList(){Student *pHead = (Student *)calloc(1, sizeof(Student));if (pHead == NULL){return NULL;}strcpy_s(pHead->szName, "head");ring_init(&pHead->item);/* 初始化环 */AppendList(pHead, "张三", "0001", 90);AppendList(pHead, "李四", "0002", 88);AppendList(pHead, "王五", "0003", 92);AppendList(pHead, "赵六", "0004", 87);return pHead;}void PrintList(Student *pHead){RingItem *link;//RING_FOREACH_REVERSED(link, &pHead->item){/* 逆序遍历 */RING_FOREACH(link, &pHead->item){Student *pStudent = SPICE_CONTAINEROF(link, Student, item);printf("name:%s No.:%s score:%d\n", pStudent->szName, pStudent->szNo, pStudent->nScore);}}void DestroyList(Student *pHead){RingItem *link;RingItem *next;/* 这里不能使用RING_FOREACH,因为会释放,需要提前记录next值,如果用RING_FOREACH会崩溃 */RING_FOREACH_SAFE(link, next, &pHead->item){Student *pStudent = SPICE_CONTAINEROF(link, Student, item);/* 通过结构体成员变量的地址,获取结构体首地址 */printf("Destroy %s\n", pStudent->szName);ring_remove(link);free(pStudent);}free(pHead);/* RING_FOREACH_SAFE遍历无法遍历到头结点 所以要单独释放 */}int _tmain(int argc, _TCHAR* argv[]){Student *pHead = InitList();if (pHead){PrintList(pHead);DestroyList(pHead);}return 0;}
    上述代码有用到2个宏,这算是阅读代码最困难的地方吧,两个宏的定义:
#define SPICE_OFFSETOF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)  #define SPICE_CONTAINEROF(ptr, type, member)  (type *)( (char *)ptr - SPICE_OFFSETOF(type,member) )
    我们通过上一个结构体可以访问下一个结构体的主要原因是上一个结构体记住了下一个结构体成员item的地址,而这两个宏的作用就是通过成员变量的地址,反推结构体的地址,知道了结构体的地址我们就能访问结构体的所有成员了。


1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 支付宝蚂蚁花呗逾期怎么办 被注销的微信怎么办 花呗有些不能用怎么办 实体店生意不好做怎么办 电器实体店生意越来越差怎么办 开业第一天不吉利怎么办 美容店开业第一天没人怎么办 淘宝店铺没有人访问怎么办 淘宝店铺没有人问怎么办 淘宝申请退款后店铺关闭怎么办 宝贝详情怎么改不了怎么办 改详情页后被删除宝贝怎么办 淘宝网商贷生意不好还不了怎么办 英国遗失在酒店物品怎么办 班福法则首位是0怎么办 同事能力比你强怎么办 新买的木板床响怎么办 笔记本键盘驱动坏了怎么办 云柜快递超时了怎么办 毕业设计被老师发现抄的怎么办 地板颜色太深了怎么办 皮质鞋子破皮了怎么办 吃了蜘蛛丝会怎么办 南京高二分班不公平怎么办? 高中分班考试没考好怎么办 实木门上的伸缩缝太深怎么办 mac点关机没反应怎么办 被压倒扁的易拉罐怎么办 白色车漏底漆了怎么办 客厅对着卧室门怎么办 老公不上进还懒怎么办 二胡按弦手指分不开怎么办 酷塑做完后疼痛怎么办 冷冻治疗后水泡破了怎么办 冷冻治疗的水泡破了怎么办? 冷冻治疗水泡破了怎么办 脚上冷冻后起泡怎么办 刺猴冷冻后起泡怎么办 隔壁太吵怎么办阴招 楼上有小孩太吵怎么办 捷达小水管睹了怎么办