读书笔记 ——《系统程序员成长计划》篇3:双链表

说明
  本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
  QQ 群 号:513683159 【相互学习】
内容来源
  《系统程序员成长计划》

上一篇:读书笔记 ——《系统程序员成长计划》篇2:封装
下一篇:读书笔记 ——《系统程序员成长计划》篇4:拥抱变化

一、双链表分类

1、专用双链表

  双链表的实现和调用耦合在一起,只能被一个调用者使用,不能单独在其他地方被重用。
  优点:
    ①性能更高。(可直接访问数据成员,省去包装函数带来的性能开销,故可提高时间性能,无需事先完整接口,生成代码更小,因此可提高空间性能
    ②依赖更少。(自己实现不依赖别人,这样移植会更加简单)
    ③实现简单。(无需考虑复杂情况,符合当前应用即可,无需提供完整接口)

2、通用双链表

  具有通用性,可多出被重复使用。
   优点:(从全局看)
    可靠性更高。(重用性高,随着每次重用而不断改进)
    ②开发效率高。(因为可重用,故开发成本随每次重用而降低)

二、如何编写一个通用双链表

1.结点数据存什么?

   ①存值:存入时复制一份数据,保存数据的指针和长度。
      (复制数据会带来性能开销,C语言中该形式链表少见)

typedef struct _DListNode
{
	struct _DListNode* prev;
	struct _DListNode* next;
	void* data;
	size_t length;
}DListNode;

   ②存指针:只保存指向对象的指针,存取效率高,存放整数时,可把void* 强制转换成整数使用,避免内存分配。

typedef struct _DListNode
{
	struct _DListNode* prev;
	struct _DListNode* next;
	void* data;
}DListNode;

2.让C++可用:

   由于C++允许同名函数,故编译器会对函数名重新编码,导致函数名与原函数名不同造成找不到函数的情况,故为让C语言实现的函数可在C++中使用,需在头文件中添加以下内容:

#ifdef __cplusplus
extern "C"{
#endif
...
#ifdef__cplusplus
#endif

3.完整接口:

   接口完整能满足各种情况需求的需要。

三、代码实现:

单文件(未封装)版

#include<stdio.h>
#include<stdlib.h>      				/malloc需调用该函数
typedef struct DNode    				/定义双链表节点结构体
{
        struct DNode* priv;     		/前驱指针
        struct DNode* next;     		/后继指针
        int data;               		/结点数据
}DNode;


int main(int argc,char* argv[]){
        DNode* head = (DNode*)malloc(sizeof(DNode));    /分配DNode数据类型大小的空间,并返回该空间首地址(malloc函数返回值为空指针类型,故需加上强制转换类型)
        head->priv = NULL;                              /头结点的前驱指针永远指向空(因为它前面没有任何结点)
        head->next = NULL;                              /头结点的后继指针暂时为空(后面会指向下一个结点首地址)
        head->data = 1;                                 /头结点数据赋值1
        DNode* dlinklist = head;                        /定义指针dlinklist,并指向头结点首地址(主要表示该双链表,后面会不断的移动指向下一个结点)
        for(int i = 2;i <= 5; i++){
                DNode* node = (DNode*)malloc(sizeof(DNode));    /创建新结点
                node->data = i;                                 /新结点赋值
                node->next = NULL;                              /新结点指向为空
                node->priv = NULL;                              /新结点指向为空

                dlinklist->next = node;                         /将新结点地址存入前结点的后继指针中
                node->priv = dlinklist;                         /将前结点的地址存入新结点的前驱指针中
                dlinklist = dlinklist->next;                    /移动链表指针,指向下一个结点
        }
        printf("%d\n",head->next->next->next->priv->data);      /输出对应结点的数值(第三个结点的值[此处]return 0;
}

实验结果:3

单文件(封装)版

#include<stdio.h>
#include<stdlib.h>				/malloc需调用该函数

typedef struct DNode{				/定义双链表节点结构体
	struct DNode* priv;			/前驱指针
	struct DNode* next;			/后继指针
	int data;				/结点数据
}DNode,*Dlinklist;


DNode* node_creat(int data){			/结点创建函数
	DNode* node = (DNode*)malloc(sizeof(DNode));
	node->priv= NULL;
	node->next=NULL;
	node->data = data;

	return node;
}
int main(int argc,char *argv[]){
	
	DNode* head = node_creat(1);		/头结点	
	
	Dlinklist dlinklist = head;		/表示链表
	int data[]={1,2,3,4,5,6,7,8,9,0};
	for(int i = 1;i < sizeof(data)/sizeof(data[0]);i++){
		DNode* DNode = node_creat(data[i]);
		dlinklist->next = DNode;	/将新结点地址存入前结点的后继指针中
		DNode->priv = dlinklist;	/将前结点的地址存入新结点的前驱指针中
		dlinklist=dlinklist->next;	/移动链表指针,指向下一个结点
	}

	printf("head  = %d\n",head->next->next->next->priv->data);
	return 0;
}

单文件(封装)(书中示例)版


#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

/*	双链表结点	*/
typedef struct _DListNode
{
	struct _DListNode* prev;	//前驱指针
	struct _DListNode* next;	//后继指针
	void* data;			//数据(空指针类型,灵活度高)
}DListNode;

/*	双链表头结点	*/
typedef struct _DList
{
	DListNode* first;		//头结点仅有一个指针指向下一个结点,无数据
}DList;

/*	返回值状态(枚举类型)	*/
typedef enum _DListRet
{
	DLIST_RET_OK,			//正常
	DLIST_RET_OOM,			//内存溢出
	DLIST_RET_STOP,		//停止
	DLIST_RET_PARAMS,		//
	DLIST_RET_FAIL			//失败
}DListRet;


typedef DListRet (*DListDataPrintFunc)(void* data);			//函数指针


/*	双链表结点创建	参数:data 为结点数据*/
static DListNode* dlist_node_create(void* data)
{
	DListNode* node = (DListNode*)malloc(sizeof(DListNode));	//分配结点空间
	if(node != NULL)				//增加健壮性,若内存分配失败则不执行。
	{
		node->prev = NULL;			//前驱指针指向为空
		node->next = NULL;			//后继指针指向为空
		node->data = data;			//将函数行参赋值
	}

	return node;					//返回结点空间的起始地址
}
/*	计算双链表长度	参数:thiz 为双链表表头指针*/	
size_t   dlist_length(DList* thiz)	
{
	size_t length = 0;				//定义length变量并初始值为0
	DListNode* iter = thiz->first;		//将头指针地址传给iter指针(双链表结点指针类型)

	while(iter != NULL)				//若指向不为空(下面还有结点),则
	{	
		length++;				//length自增
		iter = iter->next;			//继续指向下一个结点
	}

	return length;					//返回双链表长度
}

/*	销毁双链表结点	参数:node 为双链表的结点*/
static void dlist_node_destroy(DListNode* node)
{
	if(node != NULL)				//若双链表结点不为空,则
	{
		node->next = NULL;			//将前驱指针指向空(注意,一定要赋值为空,否则指针还是有指向,虽然下面会将对应空间释放)
		node->prev = NULL;			//将后继指针指向空(注意,一定要赋值为空,否则指针还是有指向,虽然下面会将对应空间释放)
		free(node);				//释放该结点空间
	}

	return;
}

/*	双链表创建(带头指针)	*/
DList* dlist_create(void)
{
	DList* thiz = (DList*)malloc(sizeof(DList));	//创建头结点大小空间
	if(thiz != NULL)				//增加健壮性,若头结点空间分配失败
	{
		thiz->first = NULL;			//给头结点后继结点指针赋值为NULL
	}

	return thiz;					//返回头结点地址(也是双链表的表头)
}

/*	获取对应结点的地址  参数:thiz 为双链表表头指针,index 为想要读取的双链表下标位置 ,fail_return_last 为?*/
static DListNode* dlist_get_node(DList* thiz, size_t index, int fail_return_last)
{
	DListNode* iter = thiz->first;				//将头结点指针地址给iter指针

	while(iter != NULL && iter->next != NULL && index > 0)	//若当前结点不为空(存在),下一个结点也不为空(存在),链表下标(大于0)
	{
		iter = iter->next;					//指向下一个结点
		index--;						//下标自减
	}

	if(!fail_return_last)						//若为fail_return_last为0则执行
	{
		iter = index > 0 ? NULL : iter;			//若下标大于0则iter指针指向NULL,小于0则iter指针指向没有变化
	}

	return iter;							//返回对应结点的地址
}
/*	双链表插入  参数:thiz 为双链表表头指针 ,index 为想要插入双链表的下标位置, data 为想要插入的数据*/
DListRet dlist_insert(DList* thiz, size_t index, void* data)
{
	DListNode* node = NULL;					//双链表结点指针指向NULL
	DListNode* cursor = NULL;					//双链表节点指针指向NULL(cursor表示游标)

	if((node = dlist_node_create(data)) == NULL)			//若双链表结点创建失败
	{
		return DLIST_RET_OOM; 					//返回内存不足溢出错误
	}

	if(thiz->first == NULL)					//若头结点后继指针为NULL(双链表表头为空,说明后面没有结点则直接将结点给表头的后继结点即可)
	{
		thiz->first = node;					//将node结点地址赋值给头结点后继指针(这边的node结点地址为上一个条件判断的中创建的结点地址[若分两句应该会清楚很多])
		return DLIST_RET_OK;					//返回双链表创建成功
	}

	cursor = dlist_get_node(thiz, index, 1);			//获取想要插入结点的地址,此时游标地址就为想要插入的地址
	
	if(index < dlist_length(thiz))				//若想要插入的结点的位置在双链表的长度里面,则
	{
		if(thiz->first == cursor)				//判断双链表表头指针与游标指针相等不相等(头指针需要的特殊处理)
		{	
			thiz->first = node;				//相等,则将node结点地址赋值给头结点的后继指针
		}
		else							//不相等
		{	
			cursor->prev->next = node;			//此时游标即为要插入的结点位置,故需要先找到前面结点,再把创建的结点连接在前面结点的后继指针上
			node->prev = cursor->prev;			//再给新插入的结点的前驱指针赋值,指向前面这个结点
		}							//以上完成了结点插入的操作,但是新插入的结点后继指针还未指向。(还有后面结点的连接)
		node->next = cursor;					//这边就是将新插入的结点的后继指针指向指向原先这个结点(现在便为后面结点)的位置
		cursor->prev = node;					//将后面结点的前继指针指向新结点
	}
	else								//若想要插入结点位置超出双链表的长度了
	{		
		cursor->next = node;					//因为能够获取得到对应的结点,故实际上是新结点插在双链表的最后一个结点位置
		node->prev = cursor;					//即进行最后结点的前驱和后继指针的插入。
	}

	return DLIST_RET_OK;						//返回成功
}

/*	双链表的追加	*/
DListRet dlist_prepend(DList* thiz, void* data)
{
	return dlist_insert(thiz, 0, data);
}
/*	双链表的附加	*/
DListRet dlist_append(DList* thiz, void* data)
{
	return dlist_insert(thiz, -1, data);
}

/*	双链表的删除	参数:为双链表表头指针 ,index 为想要删除的双链表下标位置 */
DListRet dlist_delete(DList* thiz, size_t index)
{
	DListNode* cursor = dlist_get_node(thiz, index, 0);	//获取对应下标位置并赋值给cursor(游标即为要删除的结点)

	if(cursor != NULL)					//若游标不为空,即想要删除的结点存在
	{
		if(cursor == thiz->first)			//若游标与表头地址相同
		{
			thiz->first = cursor->next;		//则表头指向要删除结点的后继结点
		}

		if(cursor->next != NULL)			//若游标的后继指针不为空
		{
			cursor->next->prev = cursor->prev;	//
		}

		if(cursor->prev != NULL)
		{
			cursor->prev->next = cursor->next;
		}

		dlist_node_destroy(cursor);
	}

	return DLIST_RET_OK;
}

DListRet dlist_get_by_index(DList* thiz, size_t index, void** data)
{
	DListNode* cursor = dlist_get_node(thiz, index, 0);

	if(cursor != NULL)
	{
		*data = cursor->data;
	}

	return cursor != NULL ? DLIST_RET_OK : DLIST_RET_FAIL;
}

DListRet dlist_set_by_index(DList* thiz, size_t index, void* data)
{
	DListNode* cursor = dlist_get_node(thiz, index, 0);

	if(cursor != NULL)
	{
		cursor->data = data;
	}

	return cursor != NULL ? DLIST_RET_OK : DLIST_RET_FAIL;
}



void dlist_destroy(DList* thiz)
{
	DListNode* iter = thiz->first;
	DListNode* next = NULL;

	while(iter != NULL)
	{
		next = iter->next;
		dlist_node_destroy(iter);
		iter = next;
	}

	thiz->first = NULL;
	free(thiz);

	return;
}


static DListRet print_int(void* data)
{
	printf("%d ", (int)data);

	return DLIST_RET_OK;
}
DListRet dlist_print(DList* thiz, DListDataPrintFunc print)
{
	DListRet ret = DLIST_RET_OK;
	DListNode* iter = thiz->first;

	while(iter != NULL)
	{
		print(iter->data);

		iter = iter->next;
	}

	return ret;
}

int main(int argc, char* argv[])
{
	int i = 0;
	int n = 100;
	DList* dlist = dlist_create();

	for(i = 0; i < n; i++)
	{
		assert(dlist_append(dlist, (void*)i) == DLIST_RET_OK);
	}
	for(i = 0; i < n; i++)
	{
		assert(dlist_prepend(dlist, (void*)i) == DLIST_RET_OK);
	}

	dlist_print(dlist, print_int);

	dlist_destroy(dlist);

	return 0;
}

多文件(封装)(书中示例)版

dlist.c
/*
 * File:    dlist.c
 * Author:  Li XianJing <xianjimli@hotmail.com>
 * Brief:   double list implementation.
 *
 * Copyright (c) Li XianJing
 *
 * Licensed under the Academic Free License version 2.1
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * History:
 * ================================================================
 * 2008-11-09 Li XianJing <xianjimli@hotmail.com> created
 *
 */
#include <stdlib.h>
#include "dlist.h"

typedef struct _DListNode
{
	struct _DListNode* prev;
	struct _DListNode* next;

	void* data;
}DListNode;

struct _DList
{
	DListNode* first;
};

/*创建双链表结点*/
static DListNode* dlist_node_create(void* data)
{
	DListNode* node = malloc(sizeof(DListNode));

	if(node != NULL)
	{
		node->prev = NULL;
		node->next = NULL;
		node->data = data;
	}

	return node;
}

static void dlist_node_destroy(DListNode* node)
{
	if(node != NULL)
	{
		node->next = NULL;
		node->prev = NULL;
		free(node);
	}

	return;
}

DList* dlist_create(void)
{
	DList* thiz = malloc(sizeof(DList));

	if(thiz != NULL)
	{
		thiz->first = NULL;
	}

	return thiz;
}

//dlist_get_node(dlist, -1, 1)
static DListNode* dlist_get_node(DList* thiz, size_t index, int fail_return_last)
{
	DListNode* iter = thiz->first;				//将结点指向的位置给iter()

	while(iter != NULL && iter->next != NULL && index > 0)	//若结点指向非空 且结点还存在下一个结点 且下标是大于0的话,不停的向下移动
	{
		iter = iter->next;					//
		index--;
	}

	if(!fail_return_last)
	{
		iter = index > 0 ? NULL : iter;
	}

	return iter;
}

//DListRet dlist_insert(dlist, -1, (void*)i);
DListRet dlist_insert(DList* thiz, size_t index, void* data)
{
	DListNode* node = NULL;			//定义双链表结点结构类型的指针,并指向NULL
	DListNode* cursor = NULL;			//定义双链表结点结构类型的指针,并指向NULL

	if((node = dlist_node_create(data)) == NULL)	//创建双链表结点空间大小并将首地址给node,若指向空则返回内存溢出错误。(内存不足)
	{
		return DLIST_RET_OOM; 
	}

	if(thiz->first == NULL)			//若头结点指向空(下面没有结点),则头结点指向node(上一条if判断语句已经指向双链表结点大小的空间[分成两句看起来就会清晰])
	{	
		thiz->first = node;		

		return DLIST_RET_OK;
	}

	cursor = dlist_get_node(thiz, index, 1);	//cursor作为游标,先获取当前链表下标的值。这边应该就是首结点的位置
	
	if(index < dlist_length(thiz))		//length获取双链表的长度(从头指针开始遍历)
	{
		if(thiz->first == cursor)		//若游标为头结点的话,则把头结点指向node(新结点)
		{
			thiz->first = node;
		}				
		else					//不是头结点的话,则
		{
			
			cursor->prev->next = node;	//头插法???
			node->prev = cursor->prev;
		}
		node->next = cursor;
		cursor->prev = node;
	}
	else
	{
		cursor->next = node;
		node->prev = cursor;
	}

	return DLIST_RET_OK;
}

DListRet dlist_prepend(DList* thiz, void* data)
{
	return dlist_insert(thiz, 0, data);
}

DListRet dlist_append(DList* thiz, void* data)
{
	return dlist_insert(thiz, -1, data);
}

DListRet dlist_delete(DList* thiz, size_t index)
{
	DListNode* cursor = dlist_get_node(thiz, index, 0);

	if(cursor != NULL)
	{
		if(cursor == thiz->first)
		{
			thiz->first = cursor->next;
		}

		if(cursor->next != NULL)
		{
			cursor->next->prev = cursor->prev;
		}

		if(cursor->prev != NULL)
		{
			cursor->prev->next = cursor->next;
		}

		dlist_node_destroy(cursor);
	}

	return DLIST_RET_OK;
}

DListRet dlist_get_by_index(DList* thiz, size_t index, void** data)
{
	DListNode* cursor = dlist_get_node(thiz, index, 0);

	if(cursor != NULL)
	{
		*data = cursor->data;
	}

	return cursor != NULL ? DLIST_RET_OK : DLIST_RET_FAIL;
}

DListRet dlist_set_by_index(DList* thiz, size_t index, void* data)
{
	DListNode* cursor = dlist_get_node(thiz, index, 0);

	if(cursor != NULL)
	{
		cursor->data = data;
	}

	return cursor != NULL ? DLIST_RET_OK : DLIST_RET_FAIL;
}

size_t   dlist_length(DList* thiz)		//获取双链表的长度,从头结点不停向下遍历
{
	size_t length = 0;
	DListNode* iter = thiz->first;

	while(iter != NULL)
	{
		length++;
		iter = iter->next;
	}

	return length;
}

DListRet dlist_print(DList* thiz, DListDataPrintFunc print)
{
	DListRet ret = DLIST_RET_OK;
	DListNode* iter = thiz->first;

	while(iter != NULL)
	{
		print(iter->data);

		iter = iter->next;
	}

	return ret;
}

void dlist_destroy(DList* thiz)
{
	DListNode* iter = thiz->first;
	DListNode* next = NULL;

	while(iter != NULL)
	{
		next = iter->next;
		dlist_node_destroy(iter);
		iter = next;
	}

	thiz->first = NULL;
	free(thiz);

	return;
}


dlist.h
/*
 * File:    dlist.h
 * Author:  Li XianJing <xianjimli@hotmail.com>
 * Brief:   double list header file.
 *
 * Copyright (c) Li XianJing
 *
 * Licensed under the Academic Free License version 2.1
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * History:
 * ================================================================
 * 2008-11-09 Li XianJing <xianjimli@hotmail.com> created
 *
 */

#ifndef DLIST_H
#define DLIST_H

/*
*	C++中允许同名函数存在,故编译器会对函数名重新编码。
*	当C++代码包含C语言头文件时,重新编码的函数没与C语言原函数名不一致,会造成找不到函数的情况。
*	故:为了C语言实现的函数可在C++中调用,需要在头文件中加上:
		#ifdef __cplusplus
		extern "C" {
		#endif
		
		...(具体内容)
		
		#ifdef __cplusplus
		}
		#endif
		
*/
#ifdef __cplusplus
extern "C" {
#endif/*__cplusplus*/


/*
*	enum 定义枚举类型(在实际编程中,数据取值有限,故最好给每个值取个名字方便使用[以下用来表示双链表创建的不同的状态])
*	默认从0开始,往后递增。也可单独赋值,若后值又不赋值,则后值都是前值递增得来
*	需注意:
*		1.这些标识符作用范围是全局(严格来说main函数内部),不能再定义同名变量
		2.这些标识符都是常量,不可对它们再赋值,只可赋值给其他变量
		3.与宏定义(预处理阶段替换)类似,枚举在编译阶段替换。故不占用数据区的内存,而是直接编译到命令中,放到代码区(无法用&取得地址)。
*/
typedef enum _DListRet			//typedef取别名,故出现DListRet 相当于 enum _DListRet
{
	DLIST_RET_OK,			//成功
	DLIST_RET_OOM,			//内存溢出(OutOfMemory)
	DLIST_RET_STOP,		//停止
	DLIST_RET_PARAMS,		//
	DLIST_RET_FAIL			//失败
}DListRet;

struct _DList;				//声明结构体_DList
typedef struct _DList DList;		//取别名:DList 相当于 struct _DList

typedef DListRet (*DListDataPrintFunc)(void* data);		//取别名(函数指针):	DListDataPrintFunc 相当于(*DListRet)(void* data)

DList* dlist_create(void);

DListRet dlist_insert(DList* thiz, size_t index, void* data);
DListRet dlist_prepend(DList* thiz, void* data);
DListRet dlist_append(DList* thiz, void* data);
DListRet dlist_delete(DList* thiz, size_t index);
DListRet dlist_get_by_index(DList* thiz, size_t index, void** data);
DListRet dlist_set_by_index(DList* thiz, size_t index, void* data);
size_t   dlist_length(DList* thiz);
DListRet dlist_print(DList* thiz, DListDataPrintFunc print);

void dlist_destroy(DList* thiz);

#ifdef __cplusplus
}
#endif/*__cplusplus*/

#endif/*DLIST*/

main.c
/*
 * File:    main.c
 * Author:  Li XianJing <xianjimli@hotmail.com>
 * Brief:   demo how to print dlist.
 *
 * Copyright (c) Li XianJing
 *
 * Licensed under the Academic Free License version 2.1
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

/*
 * History:
 * ================================================================
 * 2008-11-09 Li XianJing <xianjimli@hotmail.com> created
 *
 */

#include <stdio.h>
#include <assert.h>		//assert宏所需头文件
#include "dlist.h"

static DListRet print_int(void* data)
{
	printf("%d ", (int)data);

	return DLIST_RET_OK;
}

int main(int argc, char* argv[])
{
	int i = 0;		
	int n = 100;		
	DList* dlist = dlist_create();	//创建头结点(只有一个指针大小空间,无数据)

	for(i = 0; i < n; i++)
	{
		assert(dlist_append(dlist, (void*)i) == DLIST_RET_OK);	//assert宏原型:void assert(int expression);	作用:若条件返回错误,则终止程序 
		//append追加函数:调用insert(thiz, -1, data); 参数-1为下标,这边表示头结点的下表,之后根据插入函数插入不断赋值
	}
	for(i = 0; i < n; i++)
	{
		assert(dlist_prepend(dlist, (void*)i) == DLIST_RET_OK);
	}

	dlist_print(dlist, print_int);

	dlist_destroy(dlist);

	return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值