C语言中用宏设计的“泛型”堆栈

每次项目当要用到堆栈时要么会重新写个要么把以前的拷贝过来换个类型,一直在想能不能用C语言做个通用的数据结构库,像C++中的STL一样。要在C语言突破类型的限制以我目前的知识水平能想到的用宏了,后来查到了#define中##的用法。后来就产生了下面这段代码:

  1. #define __p_start               do{  
  2. #define __p_end             }while(0);  
  3.  
  4. #define p_stack(_s, _t, _n) typedef _t  type_##_s;          \   
  5.                                 type_##_s   _buf_##_s[_n];      \   
  6.                                 int         _max_##_s = _n-1;   \   
  7.                                 int         _top_##_s = -1  
  8.  
  9. #define p_empty(_s)             (_top_##_s < 0)  
  10. #define p_full(_s)              (_top_##_s >= _max_##_s)  
  11. #define p_push(_s, _x)          (!p_full(_s) ? (_buf_##_s[++(_top_##_s)]= _x, 1) : 0)  
  12. #define p_pop(_s)               (!p_empty(_s) ? ((_top_##_s)--, 1) : 0)  
  13. #define p_top(_s)               (!p_empty(_s) ? _buf_##_s[_top_##_s] : (type_##_s)0)  
由于C语言中的变量需要在首部声明(文件,函数,代码块首部),所以用花括号解决了任意处申明的问题,但p_stack宏必须紧跟在_p_start宏后,而且每个_p_start宏必须有个对应的_p_end宏。具体用法如下:
  1. __p_start   
  2.         p_stack(s1, int, 16);   
  3.            
  4.         for(i = 0; i < 16; i++)   
  5.             p_push(s1, i+1);   
  6.   
  7.         __p_start   
  8.             p_stack(s2, float, 16);   
  9.                
  10.             for(i = 0; i < 16; i++)   
  11.                 p_push(s2, (float)i*i / 2.0f);   
  12.   
  13.             for(i = 0; i < 16; i++)   
  14.                 printf("s1[%d]=%d, s2[%d]=%f\n", i, p_top(s1), i, p_top(s2));   
  15.         __p_end   
  16.     __p_end  

后来考虑到__p_start不能带;号与C语言语法有点冲突,改进如下:

  1. #define p_stack_start(_s, _t, _n)       do{                                 \   
  2.                                             typedef _t  type_##_s;          \   
  3.                                             type_##_s   _buf_##_s[_n];      \   
  4.                                             int         _max_##_s = _n-1;   \   
  5.                                             int         _top_##_s = -1  
  6.  
  7. #define p_stack_end(_s)                     _max_##_s = 0;_top_##_s = -1;   \   
  8.                                         }while(0)  
  9.  
  10. #define p_empty(_s)             (_top_##_s < 0)  
  11. #define p_full(_s)              (_top_##_s >= _max_##_s)  
  12. #define p_push(_s, _x)          (!p_full(_s) ? (_buf_##_s[++(_top_##_s)]= _x, 1) : 0)  
  13. #define p_pop(_s)               (!p_empty(_s) ? ((_top_##_s)--, 1) : 0)  
  14. #define p_top(_s)               (!p_empty(_s) ? _buf_##_s[_top_##_s] : (type_##_s)0)  

用法如下:

  1. p_stack_start(s1, int, 16);   
  2.         for(i = 0; i < 16; i++)   
  3.             p_push(s1, i+1);   
  4.   
  5.         for(i = 0; i < 16; i++)   
  6.         {   
  7.             printf("[%d]=%d\n", 16-i, p_top(s1));   
  8.             p_pop(s1);   
  9.         }   
  10.     p_stack_end(s1);  

每个堆栈的作用域在p_stack_start和p_end之间,目前这个还有个小bug就是关于p_top的返回如果是int型的,那么返回0就有可能和堆栈里面的数据值一样不能识别了。目前能想到的也只有此了,刚还做了个同样的队列,对于链表这个还没想好,而且也不太合适,主要是链表的代码更复杂直接用宏的话太牵强了,如果大家有更好的方法也可以共享一下。

相关推荐