环境:OpenSSL 0.9.8l,Fedora 12
今天学的是《OpenSSL编程》第3章 堆栈。这一章讲了OpenSSL中堆栈的用法,OpenSSL实现了一个通用的栈,这个栈可以存储所有的数据类型(因为栈里存的是地址)。
下面是这章中最重要的一个数据结构,STACK,这个数据结构定义在文件stack.h里:
typedef struct stack_st
{
int num; //已经进栈的元素的个数
char **data; //栈的起始地址
int sorted; //如果栈已排过序,则为1;否则为0
int num_alloc; //栈的总大小
int (*comp)(const char * const *, const char * const *); //在排序时使用的比较函数,由用户指定
} STACK;
下面是与栈操作相关的几个操作函数,在文件stack.h里声明,在文件stack.c里实现:
1. int (*sk_set_cmp_func(STACK *sk, int (*c)(const char * const *,const char * const *)))
(const char * const *, const char * const *)
这 个函数需要的参数是一个栈的指针,和一个函数指针,返回值也是一个函数指针。此函数的作用是给一个栈设定一个比较函数,并返回以前的比较函数,也就是说把 参数c赋给sk->comp,并返回以前的sk->comp。如果c和以前的sk->comp不是同一个函数,则把 sk->sorted赋为0。
2. STACK *sk_dup(STACK *sk)
此函数的作用是将sk指向的栈的内容复制到一个新栈中,如果成功则返回新的栈的地址,否则返回NULL。
3. STACK *sk_new_null(void)
此函数的作用是产生一个新的栈,并且不设定栈的比较函数,返回新栈的地址,该函数是通过函数sk_new实现的(return sk_new((int (*)(const char * const *, const char * const *))0);)。
4. STACK *sk_new(int (*c)(const char * const *, const char * const *))
此函数的作用是产生一个新栈,参数是比较函数的函数指针,此函数会为该栈预先产生MIN_NODES个sizeof(char *)的数组,并把数组首地址赋给data,然后把num的值设为0,把num_alloc的值设为MIN_NODES,把comp为值设为c,把 sorted的值设为0。如果函数执行过程中没有问题,则最后返回新的栈的指针;如果出错,则返回NULL。
5. int sk_insert(STACK *st, char *data, int loc)
此函数的作用是把data的值插入栈的第loc个元素的地方,如果成功,返回目前栈中元素的总个数,否则返回0。不过在这个函数的定义里有一点比较奇怪:
#ifdef undef /* no memmove on sunos :-( */
memmove( (char *)&(st->data[loc+1]),
(char *)&(st->data[loc]),
sizeof(char *)*(st->num-loc));
#endif
这样来注释一段语句的,还是头一回见。
6. char *sk_delete_ptr(STACK *st, char *p)
此函数的作用是从栈st中移除值为p的一项,成功则返回p,失败则返回NULL。此函数通过函数sk_delete实现。
7. char *sk_delete(STACK *st, int loc)
此函数的作用是从栈st中移除第loc个元素,成功则返回第loc个元素的内容,失败则返回NULL。
8. static int internal_find(STACK *st, char *data, int ret_val_options)
此函数是一个内部函数,不能被外部程序使用,而函数sk_find和函数sk_find_ex都是通过此函数实现的。此函数的作用是在st中查找 data,若找到则返回下标,否则返回-1。如果st没有设定比较函数,则在栈中查找与data的值一样的元素;如果st设定了比较函数,则以比较函数为 参数调用OBJ_bsearch_ex来查找,若找到则返回下标,否则返回-1。
9. int sk_find(STACK *st, char *data)
从 OBJ_BSEARCH_FIRST_VALUE_ON_MATCH的字面意思来看,这个函数的意思是找第一个匹配的元素
int sk_find(STACK *st, char *data)
{
return internal_find(st, data, OBJ_BSEARCH_FIRST_VALUE_ON_MATCH);
}
10. int sk_find_ex(STACK *st, char *data)
{
return internal_find(st, data, OBJ_BSEARCH_VALUE_ON_NOMATCH);
}
11. int sk_push(STACK *st, char *data)
此函数的作用是将data进栈,也就是把data插入到栈的最后:
int sk_push(STACK *st, char *data)
{
return(sk_insert(st,data,st->num));
}
12. int sk_unshift(STACK *st, char *data)
此函数的作用是在栈底插入一个元素。
int sk_unshift(STACK *st, char *data)
{
return(sk_insert(st,data,0));
}
13. char *sk_shift(STACK *st)
此函数的作用是从栈底删除一个元素。
char *sk_shift(STACK *st)
{
if (st == NULL) return(NULL);
if (st->num <= 0) return(NULL);
return(sk_delete(st,0));
}
14. char *sk_pop(STACK *st)
此函数的作用是从栈st中出栈一个元素:
char *sk_pop(STACK *st)
{
if (st == NULL) return(NULL);
if (st->num <= 0) return(NULL);
return(sk_delete(st,st->num-1));
}
15. void sk_zero(STACK *st)
此函数的作用是将栈中所有元素清0。
void sk_zero(STACK *st)
{
if (st == NULL) return;
if (st->num <= 0) return;
memset((char *)st->data,0,sizeof(st->data)*st->num);
st->num=0;
}
16. void sk_pop_free(STACK *st, void (*func)(void *))
此函数的作用是对栈中每个元素做func的操作,然后销毁栈。func可以是元素的清除函数。
void sk_pop_free(STACK *st, void (*func)(void *))
{
int i;
if (st == NULL) return;
for (i=0; i<st->num; i++)
if (st->data[i] != NULL)
func(st->data[i]);
sk_free(st);
}
17. void sk_free(STACK *st)
此函数的作用是释放栈所占的内存空间。
void sk_free(STACK *st)
{
if (st == NULL) return;
if (st->data != NULL) OPENSSL_free(st->data);
OPENSSL_free(st);
}
18. int sk_num(const STACK *st)
此函数的作用是返回栈中元素的个数。
19. char *sk_value(const STACK *st, int i)
此函数的作用是获取栈中第i个元素的值。
char *sk_value(const STACK *st, int i)
{
if(!st || (i < 0) || (i >= st->num)) return NULL;
return st->data[i];
}
20. char *sk_set(STACK *st, int i, char *value)
此函数的作用是把栈中第i个元素的值改为value。
char *sk_set(STACK *st, int i, char *value)
{
if(!st || (i < 0) || (i >= st->num)) return NULL;
return (st->data[i] = value);
}
21. void sk_sort(STACK *st)
此函数的作用是给栈中的元素排序,排序算法是qsort,使用的比较函数是st->comp,排完序以后,把st->sorted设为1。需要注意的是,如果没有给栈指定比较函数,而调用此函数,会产生段错误。
22. int sk_is_sorted(const STACK *st)
{
if (!st)
return 1;
return st->sorted;
}
上面是栈的操作函数,但是OpenSSL不建议我们直接去用这些函数来操作我们的栈和栈中的数据,而是建议我们去定义自己的宏来间接调用这些函数。
对于下面的例子,假设栈中保存的元素是指向结构体Student的指针,结构体Student定义如下:
typedef struct Student_st
{
char *name;
int age;
char *otherInfo;
}Student;
如果我们要使用sk_new新建一个栈,最好不要直接去使用它,而是用一个宏定义来代替它:
#define sk_Student_sk_new(cmp) SKM_sk_new(Student, (cmp))
以后要用sk_new的时候,直接用sk_Student_sk_new就可以了,不过上面这个宏里又出现了一个新东西:SKM_sk_new,其实,这也是一个宏,定义在safestack.h里:
#define SKM_sk_new(type, cmp) sk_new((int (*)(const char * const *, const char * const *))(cmp))
从这里,我们可以看出宏sk_Student_sk_new要求传入一个比较函数的函数指针,把这些宏展开我们可以看到,我们通过sk_Student_sk_new完成了对sk_new的调用。
这只是一个例子,我们以后要使用上面介绍的函数时应该通过这种宏定义来调用:
#define sk_Student_new_null() SKM_sk_new_null(Student)
#define sk_Student_free(st) SKM_sk_free(Student, (st))
#define sk_Student_num(st) SKM_sk_num(Student, (st))
...
...
...
上面的操作函数要通过宏来使用,对栈的结构的使用也要宏来定义。如果程序里需要定义一个栈,且栈中保存的元素是指向Student的指针,则需要通过下面的方式来定义一个栈的类型。
typedef STACK_OF(Student) Students;
宏STACK_OF在文件safestack.h中被定义:
#define STACK_OF(type) STACK
这样看来,其实Students就是STACK了。