当前位置: 首页 > >

OpenSSL学*笔记??堆栈

发布时间:

环境: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; inum; 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了。



友情链接: