實現一個簡單的棧,並非難事,但是使用C語言編寫一個GenericStack還是有挑戰.本文使用自增長數組的方式實現棧.同樣遵循上篇("genericlist篇")所寫的C語言泛型設計的原則,再次不贅述.
模型
------------------------------------------------------------------------------------------------------------------------
核心代碼
------------------------------------------------------------------------------------------------------------------------
完整代碼詳見我的github(https://github.com/gnudennis/ds_c)(genric-stack.h
generic-stack.c generic-stack-test.c)
0. Generic Stack定義
typedef void *ElementAddr;typedef void (*PfCbFree)(ElementAddr);typedef struct StackRecord{ ElementAddr*array; intelemsize; intloglength; intalloclength; PfCbFreefreefn;} *Stack;
1. API的設計
/* Create a new stack */Stack stack_create(int elemsize, PfCbFree freefn);/* Dispose the stack */void stack_dispose(Stack stk);/* Make the given stack empty */void stack_make_empty(Stack stk);/* Return true if the stack is empty */int stack_is_empty(Stack stk);/* Insert a new element onto stack */void stack_push(Stack stk, ElementAddr elemaddr);/* Delete the top element off the stack */void stack_pop(Stack stk);/* Fetch the top element from the stack */void stack_top(Stack stk, ElementAddr elemaddr);/* Fetch & Delete the top element from the stack */void stack_top_and_pop(Stack stk, ElementAddr elemaddr);
將pop和top分開的目的是為了滿足不同的需求,同時添加了stack_top_and_pop作為補充.
2.實現
#define MIN_STACK_SIZE (4)/* Create a new stack */Stack stack_create(int elemsize, PfCbFree freefn){ Stack stk; stk = malloc(sizeof(struct StackRecord)); if ( stk == NULL) { fprintf(stderr, "Out of memory\n"); exit(1); } stk->array = malloc(elemsize * MIN_STACK_SIZE); if (stk->array == NULL) { fprintf(stderr, "Out of memory\n"); exit(1); } stk->elemsize = elemsize; stk->loglength = 0; stk->alloclength = MIN_STACK_SIZE;}/* Dispose the stack*/void stack_dispose(Stack stk){ stack_make_empty(stk); free(stk->array); free(stk);}/* Make the given stack empty*/void stack_make_empty(Stack stk){ if ( stk->freefn ) { int i; for ( i = 0; i < stk->loglength; ++i) { stk->freefn((char *)stk->array + i * stk->elemsize); } } stk->loglength = 0;}/* Return true if the stack is empty*/int stack_is_empty(Stack stk){ return stk->loglength == 0;}static void stack_grow(Stack stk){ stk->alloclength *= 2; stk->array = realloc(stk->array, stk->alloclength * stk->elemsize);}/* Insert a new element onto stack */void stack_push(Stack stk, ElementAddr elemaddr){ ElementAddr target; if ( stk->loglength == stk->alloclength ) stack_grow(stk); target = (char *)stk->array + stk->loglength * stk->elemsize; memcpy(target, elemaddr, stk->elemsize); stk->loglength++;}/* Delete the top element off the stack */void stack_pop(Stack stk){ ElementAddr target; if ( stack_is_empty(stk) ) { fprintf(stderr, "Empty stack\n"); exit(1); } if ( stk->freefn ) { target = (char *)stk->array + (stk->loglength-1) * stk->elemsize; stk->freefn(target); } stk->loglength--;}/* Fetch the top element from the stack */void stack_top(Stack stk, ElementAddr elemaddr){ void *target = (char *)stk->array + (stk->loglength-1) * stk->elemsize; memcpy(elemaddr, target, stk->elemsize);}/* Fetch & Delete the top element from the stack */void stack_top_and_pop(Stack stk, ElementAddr elemaddr){ ElementAddr target; if ( stack_is_empty(stk) ) { fprintf(stderr, "Empty stack\n"); exit(1); } target = (char *)stk->array + (stk->loglength-1) * stk->elemsize; memcpy(elemaddr, target, stk->elemsize); stk->loglength--;}
使用和測試
------------------------------------------------------------------------------------------------------------------------
測試代碼:
#include <stdio.h>#include <stdlib.h>#include <string.h>#include "generic-stack.h"void strfreefn(ElementAddr elemaddr){ free(*(char **)elemaddr);}int main(int argc, char **argv){ Stack int_stk, str_stk; int i; char *names[] = { "C", "C++", "Jave", "C#", "Python", "PHP", "Basic", "Objective C", "Matlab", "Golang" }; /* test integer stack */ printf("Test integer stack\n"); int_stk = stack_create(sizeof(int), NULL); for ( i = 0; i < 10; ++i ) { stack_push(int_stk, &i); } while ( !stack_is_empty(int_stk) ) { int val; stack_top_and_pop(int_stk, &val); printf("%d\n", val); } stack_dispose(int_stk); /* test string stack */ printf("Test string stack\n"); str_stk = stack_create(sizeof(char *), strfreefn); for ( i = 0; i < 10; ++i ) { char *copy = strdup(names[i]); char *dest; stack_push(str_stk, ); } while ( !stack_is_empty(str_stk) ) { char *dest; stack_top_and_pop(str_stk, &dest); printf("%s\n", dest); free(dest); } stack_dispose(str_stk); return 0;}
分析:
(1). int_stack (存放對象)
(2). str_stack(存放指標)
array存放的是一系列指標,這些指標指向的實際對象需要在堆中分配.那麼最初放入stack進行管理的對象來源不定,可能是堆,棧,甚至是常量區.為了便於管理,應該先在堆中分配這些對象(來來源物件的副本:上述的strdup操作)."一個元素一旦入棧,這個元素的控制權就交由棧進行管理,一旦出棧,這個元素的控制權就交由使用者管理".