在 http://blog.csdn.net/kangguang/article/details/78314124 中 如果考虑要将在栈中保存的值限定在一定范围内应该怎么办呢?例如只允许0到9内的值push至栈中,该范围以外的值或是空值则不能被push至栈中。
如果重新再编写一个方法进行
bool pushWithRangeChenk(Stack *p,int val ,int min,int max)
{
if (val<min || max<val)
{
return false;
}
return true;
}
但是这种实现方法传递的参数太多,而且每次push的时候都要传递允许的范围,非常麻烦,稍微做一下改进,在生成栈的时候将允许的范围传递过去,代码如下:
typedef struct{
int top;
const size_t size;
int * const pBuf;
const bool needRangeCheck;
const int min;
const int max;
} Stack;
#define newStackWithRangeCheck(buf,min,max){ \
0,sizeof(buf)/sizeof(int),(buf), \
true,min,max \
}
static bool isRangeOk(const Stack *p,int val)
{
return !p->needRangeCheck || (p->min<=val && val<=p->max);
}
bool push(Stack *p, int val)
{
if (!isRangeOk(p, val) || isStackFull(p))
{
return false;
}
p->pBuf[p->top++] = val;
return true;
}
上面代码结构体里 保存了是否需要进行范围检查(needRangeCheck)的信息以及值的有效范围(min,max),并且定义了新的宏,用来生成带有范围检查功能的栈。
int buf[10];
Stack stack = newStackWithRangeCheck(buf, 0, 9);
注意:
如果只有这样简单的功能,上述方法足够应付了,但是以上代码中仍然有以下问题。
- 即使要生成的是不带范围检查功能的栈,栈内也需要保存needRangeCheck、min、max等多余的结构体成员,浪费了内存。
- 如果还想在栈内增加其他校验功能,就必须在结构体内再增加其他成员。这样就必须在结构体内保存所有检查功能的成员,push函数也会因这些功能变得臃肿,最终导致栈越来越难以应对功能的增加。
因为结构体成员 needRangeCheck、min、max 值在isRangeOk函数中被使用,将它们作为结构体成员让所有函数都可以看到是不合适的,也就是说存在作用域污染问题,所以需要把这些成员分离出去:
typedef struct{
const int min;
const int max;
}Range;
typedef struct{
int top;
const size_t size;
int * const pBuf;
const Range * const pRange;
} Stack;
#define newStackWithRangeCheck(buf,pRange){ \
0,sizeof(buf)/sizeof(int),(buf), \
pRange \
}
将范围检查分离出来的栈:
static bool isRangeOk(const Range * p,int val)
{
return p== NULL || (p->min<=val && val<=p->max);
}
bool push(Stack *p, int val)
{
if (!isRangeOk(p->pRange, val) || isStackFull(p))
{
return false;
}
p->pBuf[p->top++] = val;
return true;
}
检查功能的通用化
之前对栈加入了输入值上限和下限的检查功能,一般情况下,检查并不限于范围检查,例如,如果要求每次push到栈中的值都必须比上次的值大(虽然一般不会有这种要求),目前的代码还无法满足要求,但是只要将输入值检查变得更通用化就能解决这个问题
首先:将检查输入值的通用职责转移到结构体中:
#ifdef __cplusplus
extern "C"{
#endif
typedef struct Validator{
bool (* const validator)( struct Validator * pThis,int val);//1
void * const pData;//2
}Validator;
typedef struct{//3
const int min;
const int max;
}Range;
typedef struct{//4
int previousValue;
}PreviousValue;
typedef struct{
int top;
const size_t size;
int * const pBuf;
Validator * const pValidator;
} Stack;
//5
bool validateRange(Validator * pThis,int val);
bool validatePrevious(Validator * pThis,int val);
bool push(Stack *p, int val);
bool pop(Stack *p,int *pRet);
//6
#define rangeValidator(pRange){ \
validateRange, \
pRange \
}
#define previousValidator(pPrevious){ \
validatePrevious, \
pPrevious \
}
#define newStack(buf){ \
0,sizeof(buf)/sizeof(int),(buf) \
}
#define newStackWithRangeCheck(buf,pRange){ \
0,sizeof(buf)/sizeof(int),(buf), \
pRange \
}
#define newStackWithValidator(buf,pValidator){ \
0,sizeof(buf)/sizeof(int),(buf), \
pValidator \
}
#ifdef __cplusplus
}
#endif
1.Validator 结构体中的第一个成员是函数指针。该函数的参数为指向Validator结构体的指针和需要校验的值,并以bool类型返回校验结果。
2.第二个成员为校验是所需要的数据。由于校验类型不同,所以需要被校验的类型也不尽相同,为了能够保存任意类型数据,这里使用了void指针。
3.如果是范围检查,则2中的void指针接受的数据为Range结构体。
4.如果是push值递增的校验 则2中的void指针接受的数据为保存上次push的值的结构体。
5.函数指针所指向的校验函数,Validator的pData成员是void指针,因此先通过类型转换取出校验时所必须的值,然后进行校验处理。
6.使得结构体的生成变得更加简单的宏。
static bool isStackFull(const Stack *p)
{
return p->top == p->size;
}
static bool isStackEmpty(const Stack *p)
{
return p->top == 0;
}
bool validate(Validator * p,int val)
{
if (!p)
{
return true;
}
return p->validator(p,val);
}
bool push(Stack *p, int val)
{
if (!validate(p->pValidator, val) || isStackFull(p))
{
return false;
}
p->pBuf[p->top++] = val;
return true;
}
bool pop(Stack *p,int *pRet)
{
if (isStackEmpty(p))
{
return false;
}
* pRet = p->pBuf[--p->top];
return false;
}
bool pushWithRangeChenk(Stack *p,int val ,int min,int max)
{
if (val<min || max<val)
{
return false;
}
return true;
}
bool validateRange(Validator * pThis,int val)
{
Range * pRange = (Range *)(pThis->pData);
return pRange->min<=val && val<= pRange->max;
}
bool validatePrevious(Validator * pThis,int val)
{
PreviousValue * pPrevious =(PreviousValue *)pThis->pData;
if (val<pPrevious->previousValue)
{
return false;
}
pPrevious->previousValue = val;
return true;
}
//3.1
int buf[16];
Stack stack = newStack(buf);
push(&stack, 123);
//3.2
Range range = {0,9};
Validator validator = rangeValidator(&range);
Stack stack2 = newStackWithValidator(buf, &validator);
push(&stack2, 123);
//3.3
PreviousValue previous ={0};
Validator validator2 = previousValidator(&previous);
Stack stack3 = newStackWithValidator(buf, &validator2);
push(&stack3, 123);
return 0;