第二章 写的又好又快的秘诀

2.3 避免常见错误

  • strcpy , strcat, sprintf, 与 strncpy , strncat, snprintf,

  • sizeoff

  • valotile

  • intptr_t, uintptr_t

    这两个数据类型的定义,位于 /usr/include/stdint.h 文件中,这个文件是在 C99 中新增加的文件, /usr/include/inttypes.h C99 新增加的文件,在这个文件中有 #include<stdint.h> ,

    因此,使用时,既可以

    #include <stdint.h>
    

    又可以

    #include <inttypes.h>
    

    /usr/include/stdint.h 中定义:

    /* Types for `void *' pointers.  */
    #if __WORDSIZE == 64
    # ifndef __intptr_t_defined
    typedef long int                intptr_t;
    #  define __intptr_t_defined
    # endif
    typedef unsigned long int       uintptr_t;
    #else
    # ifndef __intptr_t_defined
    typedef int                     intptr_t;
    #  define __intptr_t_defined
    # endif
    typedef unsigned int            uintptr_t;
    #endif
    
    1. 提高程序的可移植性(在32位和64位的机器上).
    2. 定义这两个数据类型别名也是为了 void * 指针. 在C语言中,任何类型的指针都可以转换为 void * 类型,并且在将它转换回原来的类型时不会丢失信息。

2.4 自动测试

/*
 * File:    dlist.h
 */

#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*/

/*
 * File:    dlist.c
 */

#include <stdlib.h>
#include <stdint.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;
	return (intptr_t)data - (intptr_t)ctx;
}

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

	return DLIST_RET_OK;
}

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

	(*expected)--;

	return DLIST_RET_OK;
}

void test_int_dlist(void)
{

	//int s = 0;
	//int i = 0;
	//int n = 100;
	//int data = 0;
	intptr_t s = 0;
	intptr_t i = 0;
	intptr_t n = 100;
	intptr_t 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

ARCH=-m32
all:
	gcc -g ${ARCH} -fPIC -shared dlist.c -o libdlist.so 
	gcc -g ${ARCH} -DDLIST_TEST dlist.c  -o dlist_test

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