读书笔记 ——《系统程序员成长计划》篇6:写的又快又好的秘诀

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

上一篇:读书笔记 ——《系统程序员成长计划》篇5:数据放在哪?
下一篇:读书笔记 ——《系统程序员成长计划》篇7:动态数组

一、问题讨论

1、什么好与快?

  “快”是指效率高“好”是指软件质量高,两者并不是对立关系,作者认为在一定范围条件中,“快”与“好”是属于正反馈的关系,只有写的好,才可能写的快。
  这个范围是什么?达到平衡的中间质量点,而这个质量点是多少?作者给出答案是87.5%。

2、时间花在哪里?

1️⃣分析:通常是SPEC工程师(系统分析员)的任务,程序在这个过程主要是理解需求并分析如何实现,即:软件设计。
  这可靠深入学习各种常用设计方法,反复练习基本数据结构和算法。
2️⃣测试:增强程序健壮性。
3️⃣调试:;解决程序bug。

3、如何提高程序质量?

①代码阅读法

  写完程序后,大家都喜欢迫不及待的编译和运行程序,想马上看看自己的工作成果。通过编译器检查语法错误,让调试器帮你查bug,这似乎看起是顺理成章的事,实际确实又慢又烂的方法。
  作者比喻了非常贴切,就好像本来只是离家1公里的地方开会,但却方向坐各种交通工具后绕地球一圈才打到开会地点,最后到达时在感叹一句,现代的交通工具真是发达啊。
  故推荐使用代码阅读法来解决问题,不仅会发现错误,每读一遍便会有不同的收获,有进一步的改进和全新的想法,具体:
  第一遍:关注语法错误,代码排版和命名规则等问题。
  第二遍:常见编程错误,如:内存泄露、越界访问,变量初始化,函数返回值等问题。

②模拟计算机执行

  可有效地完善自己思路。还可考虑不同的输入数据和各种边界值,让程序逻辑更严谨。

③假想讲给朋友听

  讲出来,会让自己的逻辑更加清晰。这个我相信大家都会有一点点体会。

二、避免常见错误:

  常见错误具体有哪些呢?表现和后果呢?

1、内存泄露:

  在堆上分配内存,若不使用应释放掉,以便后面重用,而忘释放(C/C++不会自动回收)则内存不能重用造成了内存泄露。
  相对来说比较温和,少量并不会产生太大影响,但过多泄露则会导致后续内存分配失败,程序会因此奔溃。
  解决方法:没有用到的分配要及时释放。

2、内存越界访问

  分两种:读越界与写越界。
    读越界:读了不属于自己的数据。(若无效则直接奔溃,有效可能造成不可预料后果)
    写越界(缓冲区溢出):写入数据对别程序来说是随机,也会造成不可预料的后果。
  难点:出现的时机随机,症状随机,后果随机。故难以定位排查。
  解决方法:使用工具,最好的是编程时小心,仔细检查外部传入的参数。

举例:两个常见错误:

#include <stdlib.h>
#include <string .h>
int main(int argc,char* argv[])
{
	char str[10];
	int array [10]= {0,1,2,3,4.5,6,7,8.9};
	int data = array [10];
	array[10]= data;
	if(argc == 2)
	{
		strcpy (str,argv[1]);
	}
	return 0;
)

  问题一:array[10]不存在,造成越界错误。
  问题二:strcpy(str,argv[1]),可能造成越界错误,与外部数据数据有关。
故:除非数据在控制之内,否则不可用strcpystrcatsprintf之类的函数,而要用strncpystrncatsnprintf代替。

3、野指针

  野指针:指向已经释放掉的内存的指针。
  具体描述:当堆分配的内存不在使用,则释放掉:free(p),故内存被释放了,但p指针本身没有变化,故指向的内存仍然有效,则为野指针。
  因为指向内存后可能被重新赋值,故会造成越界错误,后果依然为不可预料。
  解决方法:释放内存后,把对应指针置为NULL。要注意,若从函数外层传入,那在函数内层置空是无用的。又如,析构函数中置空也是无效的,故应该在函数外层将指针置空。

4、访问空指针

  用空指针来判断指针的有效性

5、引用未初始化的变量

  未初始化内容是随机的(有的编译器会初始化为固定值)。故也会造成越界错误。
  解决方法:声明变量时要记得初始化。

6、不清楚指针运算

  不知道p+n是等同于(size_t)p+n*sizeof(*p)

7、结构的成员顺序变化引发的错误

  结构体初始化走捷近,没有一个成员一个成员初始化,这样默认是对结构体内存布局做了假设,一旦不一样(或结构体由第三方提供,做了变化)则会出现错误。
  解决方法:一个成员变量一个成员变量的初始化。

struct s
{
	int l;
	char* p;
};
/*危险*/
int main(int argc ,char* argv[])
{
	struct s s1={4,"abcd"};
	return 0;
}
/*安全,有的编译器可能不支持新标准*/
int main(int argc ,char* argv[])
{
	struct s s1={.l=4,.p="abcd"};
	return 0;
}
8、结构的大小变化引发的错误
struct base
{	
	int n;
};
struct s
{
	struct base b;
	int m;
};

  这个本身是没有问题的,C语言中实现继承的基础手法。但若第一个结构体是第三方提供的,第二个结构体是你自己的,第三方以DLL方分发,DLL最大的好处在于独立替换,故这样就可能会导致运行逻辑发生变化,原因是两个结构的内存布局重叠了,详细建议阅读《COM本质论》

9、分配/释放不配对

  malloc要和free配对使用,new要和delete/delete[]配对使用,
  主要是怕链接错库,造成:一个内存管理器中分配的内存,在另外一个内存管理器中释放时就会出现问题。

10、返回指向临时变量的指针

  栈里的变量都是临时的,当前函数执行完成时,相关的临时变量和参数就都被清除了。不能把指向这些临时变量的指针返回给调用者,因为这样的指针指向的数据是随机的,会给程序造成不可预料的后果。

/*	错误	*/
char* get_str(void)
{
	char str[] ={"abcd"};
	return str;
}
/*	正确	*/
char* get_str(void)
{
	char* str[] ={"abcd"};
	return str;
}

int main(int argc ,char* argc[])
{
	char *p = get_str();
	printf("%s\n",p);
	return 0;
}

具体描述:
  前提知识一:字符串指针与字符数组的区别
    char str[]为字符数组,指向栈中,每个元素的值都可通过指针改变(访问或修改)。
    char *str为字符串指针,指向程序的静态数据区(常量区),一旦定义不可改变。
  前提知识二:C语言 临时变量不能作为函数的返回值?栈内存和堆内存有什么区别?
    ①临时变量:函数调用时被压进栈中,函数退出时出栈(被销毁),占用的栈内存未被清空,但可被重新分配。故退出时若该栈内存被修改则该临时变量已为无意义的值。简单说,临时变量可作返回值。
    ②临时变量的指针:除了指向静态(static)变量、指向专门申请分配的(如用malloc)空间、指向常量区(如指向字符串"hello")、指向全局变量、指向程序代码区(如指向函数的指针)以外不可。
  错误详解:指向临时变量的指针的返回
    char str[]指向局部变量的地址,作为返回值是有可能被提前回收,若被提前回收则可能读取到不可预知的值,即可能造成不可预知的后果。

11、试图修改常量

  ①若为函数参数前加const,可通过强制类型转换绕过去,一般不会出错。
  ②若为全局常量字符串而言,即便强制绕过去仍会出错,因为它是存放在.rotate中,而.rotate不可被修改。

12、误解传值与传引用(传指针)

  定义函数时的参数为“形式参数(parameter)”,而调用函数时的参数为“实际参数(argument)”。
  调用函数的两种方式:值传递和引用传递传的到底是啥?
  ①值传递:
    形参得到实参的值,而两者属于不同的对象,互不影响。
  ②引用传递(指针指向的地址传递):
    形参是实参的引用,两者属于同一对象,对函数的修改也会影响外部的实参。

13、符号重名

  无论是函数名还是变量名
  ①若是不同作用范围,则没有问题。
  ②若是作用域有交集,如全局和局部,全局和全局之间,一定要避免,虽然可能没有报错,但可能出问题。

14、栈溢出

  注意栈空间的大小,不用有栈溢出的现象。

15、误用sizeof

  已知:sizeof获取不同类型指针,得到的大小是一样的,都为地址大小的空间。
  故:若在传递数组参数时,数组退化为指针(即按引用传递),则使用sizeof测形參数组时,得到的不是数组大小而是指针大小。

16、字节对齐

  字节对齐的目的:
    ①提高内存访问效率。
    ②可能得到数据是错误的,在有些平台(ARM7)上。
    ③造成结构大小变化,
  一般情况,编译器会保证全局变量和临时便令对齐。内存管理器会保证动态内存对齐。但还是要注意,不同类型的变量进行转换时一定要小心,如:char*强制转换为int *.

17、字节顺序

  是关于数据在物理内存中的布局问题。
  一般为两种:大端模式和小端模式。
    大端模式是高位字节数据存放在内存低地址处,低位字节数据存放在内存高地址处。
    小端模式指低位字节数据存放在内存低地址处,高位字节数据存放在内存高地址处。

18、多线程共享变量没有用valotile修饰

  关键字valotile的作用是告诉编译器,不要把变量优化到寄存器里。在开发多线程并发的软件时,如果这些线程共享一些全局变量,这些全局变量最好用valotile修饰。这样可以避免因为编译器优化而引起的错误(这样的错误非常难查)。

19、忘记函数的返回值

  函数需要返回值。如果你忘记return语句,它仍然会返回一个值,因为在i386及更高级的微处理器中,有EAX寄存器可以用来保存返回值,如果没有明确返回一个值,就会返回EAX中最后保存的内容。

三、自动测试

  孟岩老师翻译的《Design by Contract 原则与实践》,知道了写自动测试程序的方法:
    1.设计时,每个函数只完成单一的功能。(单一功能容易理解和预测)
    2.设计时,函数分查询和命令两类。
      查询:只查询对象的状态,不改变对象的状态。
      命令:只修改对象的状态,并返回操作是否成功的标志,而不会返回去对象的状态。
      以双链表为例:
        dlist_length:查询双向链表的长度,不修改双向链表的任何状态。
        dlist_delete:修改对象的状态(删除结点),并返回操作是否成功,而不返回当前长度或结点是否存在之类的状态。
    3.设计时,查询又细分为基础查询和复合查询两类。
      基础查询:只查询单一的状态。
      复合查询:可同时查询多个状态。
      以ftk为例:
        widget_get_width:返回窗口的宽度。
        widget_get_rect:返回窗口的左上角坐标,宽度和高度。

    4.实现时,检验输入数据,确认使用者正确调用了函数。
      契约式设计规定:调用者和实现者双方的责任。
      调用者:需要使用正确的参数,以保证正确的结果。
      实现者:需要检查输入参数是否违背了契约。
      如何检查参数?
        1.检查无效参数返回一个错误码。(但大多数人没有检查返回值的习惯,且每个地方都检查返回值很繁琐,且会让代码看起来乱【故一般检查关键地方,但一些无效参数可能无声无息被隐藏,出错时则代价就很大】)
        2.使用assert检查。(只有调试版本中有效[没有定义[NDEBUG],且一旦被触发,自动程序测试就死掉,无法继续验证下一个assert
          使用方法如下:

assert(thiz != NULL);
if(thiz == NULL)
{
	return DLIST_RET_INVALLD_PAPAMS;
}

        3.只打印一条警告(从glib中学到的)
          使用方法如下:

return_val_if_fail(cursor != NULL,DLIST_RET_INVALID_PAPAMS);
需定义两个宏,一个用于返回无返回值,一个用于有返回值。
#define return_if_fail(p) if(!(p))					\
	{printf("%s:%d Warning: "#p" failed.\n",		\
	__func__,__LINE__);return;}
#define return_val_if_fail(p,ret) if(!(p))			\
	{printf("%s:%d Warning: "#p" failed.\n",		\
	__func__,__LINE__);return(ret);}

    5.在测试时,用查询来验证命令。
      命令一般都有返回值,但是只检查返回值是不够的,故可使用查询函数检查对象的状态是不是我们所希望的。
      对于dlist_delete,作出如下预期:
        (1)若输入无效参数,则期望返回:DLIST_RET_INVALID_PAPAMS
        (2)若输入正确参数,则期望:函数返回DLIST_RET_OK,双向链表长度减1;删除的位置的下一个元素被移到删除的位置。
      可使用assert检查,有问题马上就暴露出来,定位错误较为容易,不需要调试器。

。。。
assert(dlist_length(dlist) == (n-i));
		assert(dlist_delete(dlist, 0) == DLIST_RET_OK);
		assert(dlist_length(dlist) == (n-i-1));
		if((i + 1) < n)
		{
			assert(dlist_get_by_index(dlist, 0, (void**)&data) == DLIST_RET_OK);
			assert((int)data == (i+1));
		}
。。。详细看本节代码

    6.测试时,用基本查询验证符合查询、结果应该是一致的。
    7.测试时,预期结果依赖其执行上下文,要按逻辑组织测试用例。
      前面调用的函数可能改变了对象的状态,为简化测试,每组测试用例开始时,都重置对象到初始状态。
    8.测试时,第一次只写基本的测试用例,以后逐渐累积,每次发现新的bug就把相应的测试用例加进去。每次修改了代码就运行一遍自动测试,保证修改没有引起其他副作用。
  以上用于应付正常模块测试,但下面三种情况仍会让你觉得比较棘手。
    (1)带有GUI的应用程序。
    (2)有随机数据输入。
    (3)多线程运行的程序。

四、保存工作

1、随时存盘
2、版本控制
3、定期备份
4、状态不好做些别的

五、测试程序

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-24 Li XianJing <xianjimli@hotmail.com> created
 * 2008-12-08 Li XianJing <xianjimli@hotmail.com> add autotest.
 *
 */
#include <stdlib.h>
#include "dlist.h"

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

	void* data;
}DListNode;

struct _DList
{
	DListNode* first;
	void* data_destroy_ctx;
	DListDataDestroyFunc data_destroy;
};

static void dlist_destroy_data(DList* thiz, void* data)
{
	if(thiz->data_destroy != NULL)
	{
		thiz->data_destroy(thiz->data_destroy_ctx, data);
	}

	return;
}

static DListNode* dlist_create_node(DList* thiz, void* data)
{
	DListNode* node = malloc(sizeof(DListNode));

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

	return node;
}

static void dlist_destroy_node(DList* thiz, DListNode* node)
{
	if(node != NULL)
	{
		node->next = NULL;
		node->prev = NULL;
		dlist_destroy_data(thiz, node->data);
		free(node);
	}

	return;
}

DList* dlist_create(DListDataDestroyFunc data_destroy, void* data_destroy_ctx)
{
	DList* thiz = malloc(sizeof(DList));

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

	return thiz;
}

static DListNode* dlist_get_node(DList* thiz, size_t index, int fail_return_last)
{
	DListNode* iter = NULL;
	
	return_val_if_fail(thiz != NULL, NULL); 

	iter = thiz->first;

	while(iter != NULL && iter->next != NULL && index > 0)
	{
		iter = iter->next;
		index--;
	}

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

	return iter;
}

DListRet dlist_insert(DList* thiz, size_t index, void* data)
{
	DListNode* node = NULL;
	DListNode* cursor = NULL;

	return_val_if_fail(thiz != NULL, DLIST_RET_INVALID_PARAMS); 

	if((node = dlist_create_node(thiz, data)) == NULL)
	{
		return DLIST_RET_OOM; 
	}

	if(thiz->first == NULL)
	{
		thiz->first = node;

		return DLIST_RET_OK;
	}

	cursor = dlist_get_node(thiz, index, 1);
	
	if(index < dlist_length(thiz))
	{
		if(thiz->first == cursor)
		{
			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);
	
	return_val_if_fail(cursor != NULL, DLIST_RET_INVALID_PARAMS); 

	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_destroy_node(thiz, 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);

	return_val_if_fail(cursor != NULL, DLIST_RET_INVALID_PARAMS); 

	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);

	return_val_if_fail(cursor != NULL, DLIST_RET_INVALID_PARAMS); 

	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 = NULL;
	
	return_val_if_fail(thiz != NULL, 0);

	iter = thiz->first;

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

	return length;
}

DListRet dlist_foreach(DList* thiz, DListDataVisitFunc visit, void* ctx)
{
	DListRet ret = DLIST_RET_OK;
	DListNode* iter = NULL;
	
	return_val_if_fail(thiz != NULL && visit != NULL, DLIST_RET_INVALID_PARAMS);

	iter = thiz->first;

	while(iter != NULL && ret != DLIST_RET_STOP)
	{
		ret = visit(ctx, iter->data);

		iter = iter->next;
	}

	return ret;
}

int      dlist_find(DList* thiz, DListDataCompareFunc cmp, void* ctx)
{
	int i = 0;
	DListNode* iter = NULL;

	return_val_if_fail(thiz != NULL && cmp != NULL, -1);

	iter = thiz->first;
	while(iter != NULL)
	{
		if(cmp(ctx, iter->data) == 0)
		{
			break;
		}
		i++;
		iter = iter->next;
	}

	return i;
}

void dlist_destroy(DList* thiz)
{
	DListNode* iter = NULL;
	DListNode* next = NULL;
	
	return_if_fail(thiz != NULL);

	iter = thiz->first;
	while(iter != NULL)
	{
		next = iter->next;
		dlist_destroy_node(thiz, iter);
		iter = next;
	}

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

	return;
}

#ifdef DLIST_TEST

#include <assert.h>

static int cmp_int(void* ctx, void* data)
{
	return (int)data - (int)ctx;
}

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

	return DLIST_RET_OK;
}

static DListRet check_and_dec_int(void* ctx, void* data)
{
	int* expected =(int*)ctx;
	assert(*expected == (int)data);

	(*expected)--;

	return DLIST_RET_OK;
}

void test_int_dlist(void)
{
	int s = 0;
	int i = 0;
	int n = 100;
	int data = 0;
	DList* dlist = dlist_create(NULL, NULL);

	for(i = 0; i < n; i++)
	{
		assert(dlist_append(dlist, (void*)i) == DLIST_RET_OK);
		assert(dlist_length(dlist) == (i + 1));
		assert(dlist_get_by_index(dlist, i, (void**)&data) == DLIST_RET_OK);
		assert(data == i);
		assert(dlist_set_by_index(dlist, i, (void*)(2*i)) == DLIST_RET_OK);
		assert(dlist_get_by_index(dlist, i, (void**)&data) == DLIST_RET_OK);
		assert(data == 2*i);
		assert(dlist_set_by_index(dlist, i, (void*)i) == DLIST_RET_OK);
		assert(dlist_find(dlist, cmp_int, (void*)i) == i);
	}

	for(i = 0; i < n; i++)
	{
		assert(dlist_get_by_index(dlist, 0, (void**)&data) == DLIST_RET_OK);
		assert(data == (i));
		assert(dlist_length(dlist) == (n-i));
		assert(dlist_delete(dlist, 0) == DLIST_RET_OK);
		assert(dlist_length(dlist) == (n-i-1));
		if((i + 1) < n)
		{
			assert(dlist_get_by_index(dlist, 0, (void**)&data) == DLIST_RET_OK);
			assert((int)data == (i+1));
		}
	}
	
	assert(dlist_length(dlist) == 0);

	for(i = 0; i < n; i++)
	{
		assert(dlist_prepend(dlist, (void*)i) == DLIST_RET_OK);
		assert(dlist_length(dlist) == (i + 1));
		assert(dlist_get_by_index(dlist, 0, (void**)&data) == DLIST_RET_OK);
		assert(data == i);
		assert(dlist_set_by_index(dlist, 0, (void*)(2*i)) == DLIST_RET_OK);
		assert(dlist_get_by_index(dlist, 0, (void**)&data) == DLIST_RET_OK);
		assert(data == 2*i);
		assert(dlist_set_by_index(dlist, 0, (void*)i) == DLIST_RET_OK);
	}

	i = n - 1;
	assert(dlist_foreach(dlist, check_and_dec_int, &i) == DLIST_RET_OK);
	
	s = dlist_length(dlist);
	for(i = 1; i < n; i++)
	{
		assert(dlist_insert(dlist, i, (void*)i) == DLIST_RET_OK);
		assert(dlist_length(dlist) == (s + i));
		assert(dlist_get_by_index(dlist, i, (void**)&data) == DLIST_RET_OK);
		assert(data == i);
		assert(dlist_set_by_index(dlist, i, (void*)(2*i)) == DLIST_RET_OK);
		assert(dlist_get_by_index(dlist, i, (void**)&data) == DLIST_RET_OK);
		assert(data == 2*i);
		assert(dlist_set_by_index(dlist, i, (void*)i) == DLIST_RET_OK);
	}

	dlist_destroy(dlist);

	return;
}

void test_invalid_params(void)
{
	printf("===========Warning is normal begin==============\n");
	assert(dlist_length(NULL) == 0);
	assert(dlist_prepend(NULL, 0) == DLIST_RET_INVALID_PARAMS);
	assert(dlist_append(NULL, 0) == DLIST_RET_INVALID_PARAMS);
	assert(dlist_delete(NULL, 0) == DLIST_RET_INVALID_PARAMS);
	assert(dlist_insert(NULL, 0, 0) == DLIST_RET_INVALID_PARAMS);
	assert(dlist_set_by_index(NULL, 0, 0) == DLIST_RET_INVALID_PARAMS);
	assert(dlist_get_by_index(NULL, 0, NULL) == DLIST_RET_INVALID_PARAMS);
	assert(dlist_find(NULL, NULL, NULL) < 0);
	assert(dlist_foreach(NULL, NULL, NULL) == DLIST_RET_INVALID_PARAMS);
	printf("===========Warning is normal end==============\n");

	return;
}

int main(int argc, char* argv[])
{
	test_int_dlist();

	test_invalid_params();

	return 0;
}
#endif


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-24 Li XianJing <xianjimli@hotmail.com> created
 * 2008-12-08 Li XianJing <xianjimli@hotmail.com> add autotest.
 *
 */

#include <stdio.h>

#ifndef DLIST_H
#define DLIST_H

#ifdef __cplusplus
extern "C" {
#endif/*__cplusplus*/

typedef enum _DListRet
{
	DLIST_RET_OK,
	DLIST_RET_OOM,
	DLIST_RET_STOP,
	DLIST_RET_INVALID_PARAMS,
	DLIST_RET_FAIL
}DListRet;

struct _DList;
typedef struct _DList DList;

typedef void     (*DListDataDestroyFunc)(void* ctx, void* data);
typedef int      (*DListDataCompareFunc)(void* ctx, void* data);
typedef DListRet (*DListDataVisitFunc)(void* ctx, void* data);

DList* dlist_create(DListDataDestroyFunc data_destroy, void* data_destroy_ctx);

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);
int      dlist_find(DList* thiz, DListDataCompareFunc cmp, void* ctx);
DListRet dlist_foreach(DList* thiz, DListDataVisitFunc visit, void* ctx);

void dlist_destroy(DList* thiz);

#define return_if_fail(p) if(!(p)) \
	{printf("%s:%d Warning: "#p" failed.\n", \
		__func__, __LINE__); return;}
#define return_val_if_fail(p, ret) if(!(p)) \
	{printf("%s:%d Warning: "#p" failed.\n",\
	__func__, __LINE__); return (ret);}

#ifdef __cplusplus
}
#endif/*__cplusplus*/

#endif/*DLIST*/

Makefile

all:
	gcc -g -shared dlist.c -o libdlist.so
	gcc -g -DDLIST_TEST dlist.c  -o dlist_test

clean:
	rm -f *test *.exe *.so

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值