引言

  这里需要分享的是一个 简单字符串库和 链表的基库,代码也许用到特定技巧.有时候回想一下,

如果我读书的时候有人告诉我这些关于C开发的积淀, 那么会走的多直啊.刚参加工作的时候做桌面开发,

服务是C++写,界面是C#写.那时候刚进去评级我是中级,因为他问我关于系统锁和信号量都答出来.开发一段

时间,写C#也写的很溜.后面招我那个人让我转行就写C++和php,那时候就开始学习C++有关知识.

后面去四川工作了,开发安卓,用eclipse + java语法 + android jdk,开发前端,用起来,我的感受,都相似,就是api名字

有点长. 都是那老套路,后来四川公司黄了. 辗转又来了北京做C系列还有php开发. 说了这么多, 我想说下面几个问题.

  1. 你写的是 C# 和 java吗,还只是.net/jdk的积木 , 写了那么多这样的代码,你感到疑惑吗?

  2.假如你也感到过疑惑, 推荐去看看 Linux程序开发 或 unix环境编程, 网络编程

//2.1 不推荐认真学C++, 学了好多,看了好多书,还是不明觉历,觉得是在杂耍! 如果喜欢C,把市面上好的C书都看一遍,敲一遍!

3. 因为随着年纪增长,效率太重要了, 需要去找到学到那些一招鲜吃遍天的东西, 其它的让年起人去拼搏吧.

有时候想想,做软件开发,初中就够了,高中绰绰有余,大学研究生都暴遣天物. 大家觉得呢.

又扯了一会儿蛋, 今天分享的还是很有用的,但是感觉没接触这样黑科技的还是有点难. 或者说封装一个框架还是有难度的,

或者,哪怕再小的一个库封装完毕都是不容易的.而我们分享的是封装库的库. 个人比较水,打脸要轻打.

用到的资源

  list 测试demo http://download.csdn.net/detail/wangzhione/9428243

入行第一篇博文 C的回归(国内超一线)   http://blog.codingnow.com/2007/09/c_vs_cplusplus.html

再扯一点, 2015 北京平均工资最高的三个职业 IT 特殊服务业 电子设备. 如果你穷你真的需要 认真学习编程,不要太沉迷于框架的学习中.

真的 人穷就应该多编程, 别人抢红包,你 需要写代码, , ,

  这篇博文分享的框架后面都加了一点内容, 也简单补充一下. 内容很多基本都是垃圾. 首先 以一个 不区分大小写的函数压马路.

  1. /*
  2. * 这是个不区分大小写的比较函数
  3. * ls : 左边比较字符串
  4. * rs : 右边比较字符串
  5. * : 返回 ls>rs => >0 ; ls = rs => 0 ; ls<rs => <0
  6. */
  7. extern int str_icmp(const char* ls, const char* rs);

构造如下, 看完这里基本就可以关闭,毕竟后面更啰嗦!

  1. /*
  2. * 这是个不区分大小写的比较函数
  3. * ls : 左边比较字符串
  4. * rs : 右边比较字符串
  5. * : 返回 ls>rs => >0 ; ls = rs => 0 ; ls<rs => <0
  6. */
  7. int
  8. str_icmp(const char* ls, const char* rs)
  9. {
  10. int l, r;
  11. if(!ls || !rs)
  12. return (int)ls - (int)rs;
  13.  
  14. do {
  15. if((l=*ls++)>='a' && l<='z')
  16. l -= 'a' - 'A';
  17. if((r=*rs++)>='a' && r<='z')
  18. r -= 'a' - 'A';
  19. } while(l && l==r);
  20.  
  21. return l-r;
  22. }

到这里 基本上就值了. 学到上面函数 也算温故C 基础吧! O(∩_∩)O哈哈~

前言

  终于到这里了,扯的有点多. 首先来看一下今天主要写的通用链表的接口,看设计

  1. #ifndef _H_LIST
  2. #define _H_LIST
  3.  
  4. #include <schead.h>
  5.  
  6. /*
  7. * 这个万能单链表库 前提所有结点都是堆上分配的,设计的比较老了,能用
  8. *注意
  9. * 1.使用的时候,需要加上 _LIST_HEAD; 宏
  10. * 2.创建的第一句话就是 list head = NULL; 开始从空链表开始list的生涯
  11. */
  12.  
  13. struct __lnode {
  14. struct __lnode* next;
  15. };
  16.  
  17. // 不多说了一定放在想使用链表结构的结构体头部
  18. #define _LIST_HEAD \
  19. struct __lnode __ln;
  20.  
  21. // 简单链表结构, 当你使用这个链表的时候 需要 list_t head = NULL; 开始使用之旅
  22. typedef void* list_t;
  23.  
  24. /*
  25. * 采用头查法插入结点, 第一使用需要 list_t head = NULL;
  26. *返回 _RT_OK 表示成功!
  27. * ph : 指向头结点的指针
  28. * node : 待插入的结点对象
  29. */
  30. extern int list_add(list_t* ph, void* node);
  31.  
  32. /*
  33. * 链表中查找函数,查找失败返回NULL,查找成功直接返回那个结点,推荐不要乱改,否则就崩了.
  34. *如果需要改的话,推荐 用 list_findpop, 找到并弹出
  35. * h : 链表头结点
  36. * cmp : 查找的比较函数
  37. * left : cmp(left, right) 用的左结点
  38. * : 返回查找的结点对象
  39. */
  40. extern void* list_find(list_t h, icmp_f cmp, const void* left);
  41.  
  42. /*
  43. * 查找到要的结点,并弹出,需要你自己回收
  44. * ph : 指向头结点的指针
  45. * cmp : 比较函数,将left同 *ph中对象按个比较
  46. * left : cmp(left, x) 比较返回 0 >0 <0
  47. * : 找到了退出/返回结点, 否则返回NULL
  48. */
  49. extern void* list_findpop(list_t *ph, icmp_f cmp, const void* left);
  50.  
  51. /*
  52. * 这里获取当前链表长度, 推荐调用一次就记住len
  53. * h : 当前链表的头结点
  54. * : 返回 链表长度 >=0
  55. */
  56. extern int list_len(list_t h);
  57.  
  58. /*
  59. * 查找索引位置为idx的结点,找不见返回NULL
  60. * h : 当前结点
  61. * idx : 查找的索引值[0,len)
  62. * : 返回查到的结点,如果需要删除的推荐调用 list_pop(&h, idx);
  63. */
  64. extern void* list_get(list_t h, int idx);
  65.  
  66. /*
  67. * 按照索引弹出并返回结点, 需要自己回收这个结点 推荐 free(list_pop...);
  68. * ph : 指向链表结点的指针
  69. * idx : 弹出的索引
  70. * return : 无效的弹出,返回NULL
  71. */
  72. void* list_pop(list_t* ph, int idx);
  73.  
  74. /*
  75. * 返回结点node 的上一个结点,如果node = NULL, 返回最后一个结点
  76. * h : 当前链表结点
  77. * node : 待查找的结点信息
  78. * return : 返回查找到的结点,不存在返回NULL
  79. */
  80. void* list_front(list_t h, void* node);
  81.  
  82. /*
  83. * 这个宏推荐不使用, 主要返回结点n的下一个结点
  84. * 第一种使用方法 node->next = (void*)list_node(n), 另一种是 list_node(n) = node;
  85. * n : 当前结点
  86. */
  87. #define list_next(n) \
  88. (((struct __lnode*)n)->next)
  89.  
  90. /*
  91. * 和 list_add 功能相似,但是插入位置在尾巴那
  92. * ph : 待插入结点的指针
  93. * node : 待插入的当前结点
  94. */
  95. int list_addlast(list_t* ph, void* node);
  96.  
  97. /*
  98. * 在链表的第idx索引处插入结点,也必须需要 list_t head = NULL; 在idx过大的时候
  99. *插入尾巴处,如果<0直接返回 _RT_EP. 成功了返回 _RT_OK
  100. * ph : 指向头结点的指针
  101. * idx : 结点的索引处
  102. * node : 待插入的结点
  103. */
  104. int list_addidx(list_t* ph, int idx, void* node);
  105.  
  106. /*
  107. * 这里的销毁函数,只有这些数据都是栈上的才推荐这么做,会自动让其指向NULL
  108. * ph : 指向当前链表结点的指针
  109. */
  110. void list_destroy(list_t* ph);
  111.  
  112. #endif // !_H_LIST

这里接口使用的 extern声明的希望外部直接使用, 没有extern的外部可以使用,属于扩展功能.
对于上面接口 简单的测试 代码如下

  1. #include <list.h>
  2.  
  3. struct lint {
  4. _LIST_HEAD;
  5. int node;
  6. };
  7.  
  8. //简单创建函数
  9. static struct lint* __lint_new(int node)
  10. {
  11. struct lint* ln = malloc(sizeof(struct lint));
  12. if(ln){
  13. ln->node = node;
  14. }
  15. return ln;
  16. }
  17.  
  18. //简单打印函数
  19. static void __lint_puts(list_t head)
  20. {
  21. int len = list_len(head);
  22. int i;
  23.  
  24. printf("当前链表中数据结果如下:");
  25. for(i=; i<len; ++i){
  26. struct lint* tl = list_get(head, i);
  27. printf("%d ", tl->node);
  28. }
  29. putchar('\n');
  30. }
  31.  
  32. /*
  33. * 这里简单测试一下 关于链表的常用接口
  34. */
  35. int main(int argc, char* argv[])
  36. {
  37. list_t head = NULL;
  38. int arrs[] = { , , , , , , , };
  39. int i;
  40.  
  41. //这里添加结点
  42. for(i=; i<sizeof(arrs)/sizeof(*arrs); ++i)
  43. list_add(&head, __lint_new(arrs[i]));
  44.  
  45. __lint_puts(head);
  46. //这里删除一个结点
  47. free(list_pop(&head, ));
  48. __lint_puts(head);
  49.  
  50. //删除第二个结点
  51. free(list_pop(&head, ));
  52. __lint_puts(head);
  53.  
  54. list_destroy(&head);
  55. return ;
  56. }

测试了几个简答接口. 注释比较详细, 写的也比较简单相对于Linux内核的数据结构而言. 这里是个开门红.
临摹几遍都能理解C接口的简单设计.

正文

  其实呀上面代码主要突出一个设计, 实现而言还是比较容易,因为结构有了,算法就能够写好了. 例如 获取某个结点的源码

  1. /*
  2. * 这里获取当前链表长度, 推荐调用一次就记住len
  3. * h : 当前链表的头结点
  4. * : 返回 链表长度 >=0
  5. */
  6. int list_len(list_t h)
  7. {
  8. int len = ;
  9. while(h){
  10. ++len;
  11. h = list_next(h);
  12. }
  13. return len;
  14. }

很基础也容易理解, 大多数代码其实结构设计好实现也就是时间问题, 也等同于业务了. 精妙的东西没有那么多, 魔鬼藏在细节里.向那些这个池那个组,都名次解释.
很普通.现在我们只谈设计, 最后会给出完整的代码. 同样还有一种结构, (状态不好,加班太多了,写的很水望见谅,因为很多东西说出来还是做不出来,做出来说的不好.)

看下面关于简单字符串设计代码

  1. #ifndef _H_TSTRING
  2. #define _H_TSTRING
  3.  
  4. #include <schead.h>
  5.  
  6. //------------------------------------------------简单字符串辅助操作----------------------------------
  7.  
  8. /*
  9. * 主要采用jshash 返回计算后的hash值
  10. * 不冲突率在 80% 左右还可以, 不要传入NULL
  11. */
  12. extern unsigned str_hash(const char* str);
  13.  
  14. //------------------------------------------------简单文本字符串辅助操作----------------------------------
  15.  
  16. #ifndef _STRUCT_TSTRING
  17. #define _STRUCT_TSTRING
  18. //简单字符串结构,并定义文本字符串类型tstring
  19. struct tstring {
  20. char* str; //字符串实际保存的内容
  21. int len; //当前字符串大小
  22. int size; //字符池大小
  23. };
  24. typedef struct tstring* tstring;
  25. #endif // !_STRUCT_TSTRING
  26.  
  27. //文本串栈上创建内容,不想用那些技巧了,就这样吧
  28. #define TSTRING_CREATE(var) \
  29. struct tstring var = { NULL, , }
  30. #define TSTRING_DESTROY(var) \
  31. free(var.str)
  32.  
  33. /*
  34. * tstring 的创建函数, 会根据str创建一个 tstring结构的字符串
  35. *
  36. * str : 待创建的字符串
  37. *
  38. * ret : 返回创建好的字符串,如果创建失败返回NULL
  39. */
  40. extern tstring tstring_create(const char* str);
  41.  
  42. /*
  43. * tstring 完全销毁函数
  44. * tstr : 指向tsting字符串指针量的指针
  45. */
  46. extern void tstring_destroy(tstring* tstr);
  47.  
  48. /*
  49. * 向简单文本字符串tstr中添加 一个字符c
  50. * tstr : 简单字符串对象
  51. * c : 待添加的字符
  52. * ret : 返回状态码 见 schead 中 _RT_EB 码等
  53. */
  54. extern int tstring_append(tstring tstr, int c);
  55.  
  56. /*
  57. * 向简单文本串中添加只读字符串
  58. * tstr : 文本串
  59. * str : 待添加的素材串
  60. * ret : 返回状态码主要是 _RT_EP _RT_EM
  61. */
  62. extern int tstring_appends(tstring tstr, const char* str);
  63.  
  64. //------------------------------------------------简单文件辅助操作----------------------------------
  65.  
  66. /*
  67. * 简单的文件帮助类,会读取完毕这个文件内容返回,失败返回NULL.
  68. * 需要事后使用 tstring_destroy(&ret); 销毁这个字符串对象
  69. * path : 文件路径
  70. * ret : 返回创建好的字符串内容,返回NULL表示读取失败
  71. */
  72. extern tstring file_malloc_readend(const char* path);
  73.  
  74. /*
  75. * 文件写入,没有好说的,会返回 _RT_EP _RT_EM _RT_OK
  76. * path : 文件路径
  77. * str : 待写入的字符串
  78. * ret : 返回写入的结果
  79. */
  80. extern int file_writes(const char* path, const char* str);
  81.  
  82. /*
  83. * 文件追加内容, 添加str内同
  84. * path : 文件路径
  85. * str : 待追加的文件内同
  86. * : 返回值,主要是 _RT_EP _RT_EM _RT_OK 这些状态
  87. */
  88. extern int file_append(const char* path, const char* str);
  89.  
  90. #endif // !_H_TSTRING

这个串可以用在读取一个大串,主要解决的问题是内存空间分配问题,还可以用.最大浪费就50%.

现在我们简单说一下具体实现,其实一看

  1. #ifndef _STRUCT_TSTRING
  2. #define _STRUCT_TSTRING
  3. //简单字符串结构,并定义文本字符串类型tstring
  4. struct tstring {
  5. char* str; //字符串实际保存的内容
  6. int len; //当前字符串大小
  7. int size; //字符池大小
  8. };
  9. typedef struct tstring* tstring;
  10. #endif // !_STRUCT_TSTRING

全部明白了. 就是 len表现当前str中保存的长度, size表现当前str的容量.分配代码如下

  1. //简单分配函数,智力一定会分配内存的, len > size的时候调用这个函数
  2. static int __tstring_realloc(tstring tstr, int len)
  3. {
  4. int size = tstr->size;
  5. for (size = size < _INT_TSTRING ? _INT_TSTRING : size; size < len; size <<= )
  6. ;
  7. //分配内存
  8. char *nstr = realloc(tstr->str, size);
  9. if (NULL == nstr) {
  10. SL_NOTICE("realloc(tstr->str:0x%p, size:%d) is error!", tstr->str, size);
  11. return _RT_EM;
  12. }
  13. tstr->str = nstr;
  14. tstr->size = size;
  15. return _RT_OK;
  16. }

len是新的str大小.后面展现 全部的演示代码.

  1. #include <tstring.h>
  2. #include <sclog.h>
  3.  
  4. /*
  5. * 主要采用jshash 返回计算后的hash值
  6. * 不冲突率在 80% 左右还可以, 不要传入NULL
  7. */
  8. unsigned
  9. str_hash(const char* str)
  10. {
  11. size_t i, h = strlen(str), sp = (h >> ) + ;
  12. unsigned char* ptr = (unsigned char*)str;
  13.  
  14. for (i = h; i >= sp; i -= sp)
  15. h ^= ((h<<) + (h>>) + ptr[i-]);
  16.  
  17. return h ? h : ;
  18. }
  19.  
  20. /*
  21. * tstring 的创建函数, 会根据str创建一个 tstring结构的字符串
  22. *
  23. * str : 待创建的字符串
  24. *
  25. * ret : 返回创建好的字符串,如果创建失败返回NULL
  26. */
  27. tstring
  28. tstring_create(const char* str)
  29. {
  30. tstring tstr = calloc(, sizeof(struct tstring));
  31. if (NULL == tstr) {
  32. SL_NOTICE("calloc is sizeof struct tstring error!");
  33. return NULL;
  34. }
  35. tstring_appends(tstr, str);
  36.  
  37. return tstr;
  38. }
  39.  
  40. /*
  41. * tstring 完全销毁函数
  42. * tstr : 指向tsting字符串指针量的指针
  43. */
  44. void tstring_destroy(tstring* tstr)
  45. {
  46. if (tstr && *tstr) { //展现内容
  47. free((*tstr)->str);
  48. free(*tstr);
  49. *tstr = NULL;
  50. }
  51. }
  52.  
  53. //文本字符串创建的度量值
  54. #define _INT_TSTRING (32)
  55.  
  56. //简单分配函数,智力一定会分配内存的, len > size的时候调用这个函数
  57. static int __tstring_realloc(tstring tstr, int len)
  58. {
  59. int size = tstr->size;
  60. for (size = size < _INT_TSTRING ? _INT_TSTRING : size; size < len; size <<= )
  61. ;
  62. //分配内存
  63. char *nstr = realloc(tstr->str, size);
  64. if (NULL == nstr) {
  65. SL_NOTICE("realloc(tstr->str:0x%p, size:%d) is error!", tstr->str, size);
  66. return _RT_EM;
  67. }
  68. tstr->str = nstr;
  69. tstr->size = size;
  70. return _RT_OK;
  71. }
  72.  
  73. /*
  74. * 向简单文本字符串tstr中添加 一个字符c
  75. * tstr : 简单字符串对象
  76. * c : 待添加的字符
  77. * ret : 返回状态码 见 schead 中 _RT_EM 码等
  78. */
  79. int tstring_append(tstring tstr, int c)
  80. {
  81. //不做安全检查
  82. int len = tstr->len + ; // c + '\0' 而len只指向 字符串strlen长度
  83.  
  84. //需要进行内存分配,唯一损失
  85. if ((len > tstr->size) && (_RT_EM == __tstring_realloc(tstr, len)))
  86. return _RT_EM;
  87.  
  88. tstr->len = --len;
  89. tstr->str[len - ] = c;
  90. tstr->str[len] = '\0';
  91.  
  92. return _RT_OK;
  93. }
  94.  
  95. /*
  96. * 向简单文本串中添加只读字符串
  97. * tstr : 文本串
  98. * str : 待添加的素材串
  99. * ret : 返回状态码主要是 _RT_EP _RT_EM
  100. */
  101. int tstring_appends(tstring tstr, const char* str)
  102. {
  103. int len;
  104. if (!tstr || !str || !*str) {
  105. SL_NOTICE("check param '!tstr || !str || !*str'");
  106. return _RT_EP;
  107. }
  108.  
  109. len = tstr->len + strlen(str) + ;
  110. if ((len > tstr->size) && (_RT_EM == __tstring_realloc(tstr, len)))
  111. return _RT_EM;
  112.  
  113. //这里复制内容
  114. strcpy(tstr->str + tstr->len, str);
  115. tstr->len = len - ;
  116.  
  117. return _RT_OK;
  118. }
  119.  
  120. //------------------------------------------------简单文件辅助操作----------------------------------
  121.  
  122. /*
  123. * 简单的文件帮助类,会读取完毕这个文件内容返回,失败返回NULL.
  124. * 需要事后使用 tstring_destroy(&ret); 销毁这个字符串对象
  125. * path : 文件路径
  126. * ret : 返回创建好的字符串内容,返回NULL表示读取失败
  127. */
  128. tstring file_malloc_readend(const char* path)
  129. {
  130. int c;
  131. tstring tstr;
  132. FILE* txt = fopen(path, "r");
  133. if (NULL == txt) {
  134. SL_NOTICE("fopen r path = '%s' error!", path);
  135. return NULL;
  136. }
  137.  
  138. //这里创建文件对象,创建失败直接返回
  139. if ((tstr = tstring_create(NULL)) == NULL) {
  140. fclose(txt);
  141. return NULL;
  142. }
  143.  
  144. //这里读取文本内容
  145. while ((c = fgetc(txt))!=EOF)
  146. if (_RT_OK != tstring_append(tstr, c))
  147. break;
  148.  
  149. fclose(txt);//很重要创建了就要释放,否则会出现隐藏的句柄bug
  150. return tstr;
  151. }
  152.  
  153. /*
  154. * 文件写入,没有好说的,会返回 _RT_EP _RT_EM _RT_OK
  155. * path : 文件路径
  156. * str : 待写入的字符串
  157. * ret : 返回写入的结果
  158. */
  159. int file_writes(const char* path, const char* str)
  160. {
  161. FILE* txt;
  162. //检查参数问题
  163. if (!path || !str) {
  164. SL_NOTICE("check is '!path || !str'");
  165. return _RT_EP;
  166. }
  167.  
  168. if ((txt = fopen(path, "w")) == NULL) {
  169. SL_NOTICE("fopen w path = '%s' error!", path);
  170. return _RT_EF;
  171. }
  172.  
  173. //这里写入信息
  174. fputs(str, txt);
  175.  
  176. fclose(txt);
  177. return _RT_OK;
  178. }
  179.  
  180. /*
  181. * 文件追加内容, 添加str内同
  182. * path : 文件路径
  183. * str : 待追加的文件内同
  184. * : 返回值,主要是 _RT_EP _RT_EM _RT_OK 这些状态
  185. */
  186. int
  187. file_append(const char* path, const char* str)
  188. {
  189. FILE* txt;
  190. //检查参数问题
  191. if (!path || !str) {
  192. SL_NOTICE("check is '!path || !str'");
  193. return _RT_EP;
  194. }
  195. if ((txt = fopen(path, "a")) == NULL) {
  196. SL_NOTICE("fopen a path = '%s' error!", path);
  197. return _RT_EF;
  198. }
  199. //这里写入信息
  200. fputs(str, txt);
  201.  
  202. fclose(txt);
  203. return _RT_OK;
  204. }

相比云风的那个玩具要简单的多,而且针对性很强,就为了大字符串. 转存用.还可以一试.

到这里就到了今天一个主题. 主要测试list demo. 首先看运行的结果图

首先看Makefile 文件

  1. main.out:main.c list.c schead.c
  2. gcc -g -Wall -o $@ $^ -I.

再看schead.h 文件

  1. #ifndef _H_CHEAD
  2. #define _H_CHEAD
  3.  
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <stdbool.h>
  7. #include <errno.h>
  8. #include <string.h>
  9. #include <time.h>
  10. #include <stdint.h>
  11. #include <stddef.h>
  12.  
  13. /*
  14. * 1.0 错误定义宏 用于判断返回值状态的状态码 _RF表示返回标志
  15. * 使用举例 :
  16. int flag = scconf_get("pursue");
  17. if(flag != _RT_OK){
  18. sclog_error("get config %s error! flag = %d.", "pursue", flag);
  19. exit(EXIT_FAILURE);
  20. }
  21. * 这里是内部 使用的通用返回值 标志
  22. */
  23. #define _RT_OK (0) //结果正确的返回宏
  24. #define _RT_EB (-1) //错误基类型,所有错误都可用它,在不清楚的情况下
  25. #define _RT_EP (-2) //参数错误
  26. #define _RT_EM (-3) //内存分配错误
  27. #define _RT_EC (-4) //文件已经读取完毕或表示链接关闭
  28. #define _RT_EF (-5) //文件打开失败
  29.  
  30. /*
  31. * 1.1 定义一些 通用的函数指针帮助,主要用于基库的封装中
  32. * 有构造函数, 释放函数, 比较函数等
  33. */
  34. typedef void* (*pnew_f)();
  35. typedef void(*vdel_f)(void* node);
  36. // icmp_f 最好 是 int cmp(const void* ln,const void* rn); 标准结构
  37. typedef int(*icmp_f)();
  38.  
  39. /*
  40. * 1.2 最简单的 判断字符串是否为空白字符代码, true为真
  41. */
  42. #define sh_isspace(c) \
  43. (c==' '||c=='\t'||c=='\n'||c=='\r'||c=='\v'||c=='\f')
  44.  
  45. /*
  46. * 2.0 如果定义了 __GNUC__ 就假定是 使用gcc 编译器,为Linux平台
  47. * 否则 认为是 Window 平台,不可否认宏是丑陋的
  48. */
  49. #if defined(__GNUC__)
  50. //下面是依赖 Linux 实现,等待毫秒数
  51. #include <unistd.h>
  52. #include <sys/time.h>
  53. #define SLEEPMS(m) \
  54. usleep(m * )
  55. #else
  56. // 这里创建等待函数 以毫秒为单位 , 需要依赖操作系统实现
  57. #include <Windows.h>
  58. #include <direct.h> // 加载多余的头文件在 编译阶段会去掉
  59. #define inline __inline //附加一个内联函数宏
  60. #define rmdir _rmdir
  61.  
  62. /**
  63. * Linux sys/time.h 中获取时间函数在Windows上一种移植实现
  64. **tv : 返回结果包含秒数和微秒数
  65. **tz : 包含的时区,在window上这个变量没有用不返回
  66. ** : 默认返回0
  67. **/
  68. extern int gettimeofday(struct timeval* tv, void* tz);
  69.  
  70. //为了解决 不通用功能
  71. #define localtime_r(t, tm) localtime_s(tm, t)
  72.  
  73. #define SLEEPMS(m) \
  74. Sleep(m)
  75. #endif /*__GNUC__ 跨平台的代码都很丑陋 */
  76.  
  77. //3.0 浮点数据判断宏帮助, __开头表示不希望你使用的宏
  78. #define __DIFF(x, y) ((x)-(y)) //两个表达式做差宏
  79. #define __IF_X(x, z) ((x)<z&&(x)>-z) //判断宏,z必须是宏常量
  80. #define EQ(x, y, c) EQ_ZERO(__DIFF(x,y), c) //判断x和y是否在误差范围内相等
  81.  
  82. //3.1 float判断定义的宏
  83. #define _FLOAT_ZERO (0.000001f) //float 0的误差判断值
  84. #define EQ_FLOAT_ZERO(x) __IF_X(x,_FLOAT_ZERO) //float 判断x是否为零是返回true
  85. #define EQ_FLOAT(x, y) EQ(x, y, _FLOAT_ZERO) //判断表达式x与y是否相等
  86.  
  87. //3.2 double判断定义的宏
  88. #define _DOUBLE_ZERO (0.000000000001) //double 0误差判断值
  89. #define EQ_DOUBLE_ZERO(x) __IF_X(x,_DOUBLE_ZERO) //double 判断x是否为零是返回true
  90. #define EQ_DOUBLE(x,y) EQ(x, y, _DOUBLE_ZERO) //判断表达式x与y是否相等
  91.  
  92. //4.0 控制台打印错误信息, fmt必须是双引号括起来的宏
  93. #ifndef CERR
  94. #define CERR(fmt, ...) \
  95. fprintf(stderr,"[%s:%s:%d][error %d:%s]" fmt "\r\n",\
  96. __FILE__, __func__, __LINE__, errno, strerror(errno),##__VA_ARGS__)
  97. #endif/* !CERR */
  98.  
  99. //4.1 控制台打印错误信息并退出, t同样fmt必须是 ""括起来的字符串常量
  100. #ifndef CERR_EXIT
  101. #define CERR_EXIT(fmt,...) \
  102. CERR(fmt,##__VA_ARGS__),exit(EXIT_FAILURE)
  103. #endif/* !ERR */
  104.  
  105. #ifndef IF_CERR
  106. /*
  107. *4.2 if 的 代码检测
  108. *
  109. * 举例:
  110. * IF_CERR(fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP), "socket create error!");
  111. * 遇到问题打印日志直接退出,可以认为是一种简单模板
  112. * code : 要检测的代码
  113. * fmt : 必须是""括起来的字符串宏
  114. * ... : 后面的参数,参照printf
  115. */
  116. #define IF_CERR(code, fmt, ...) \
  117. if((code) < ) \
  118. CERR_EXIT(fmt, ##__VA_ARGS__)
  119. #endif //!IF_CERR
  120.  
  121. //5.0 获取数组长度,只能是数组类型或""字符串常量,后者包含'\0'
  122. #ifndef LEN
  123. #define LEN(arr) \
  124. (sizeof(arr)/sizeof(*(arr)))
  125. #endif/* !ARRLEN */
  126.  
  127. //6.0 程序清空屏幕函数
  128. #ifndef CONSOLE_CLEAR
  129. #ifndef _WIN32
  130. #define CONSOLE_CLEAR() \
  131. system("printf '\ec'")
  132. #else
  133. #define CONSOLE_CLEAR() \
  134. system("cls")
  135. #endif/* _WIN32 */
  136. #endif /*!CONSOLE_CLEAR*/
  137.  
  138. //7.0 置空操作
  139. #ifndef BZERO
  140. //v必须是个变量
  141. #define BZERO(v) \
  142. memset(&v,,sizeof(v))
  143. #endif/* !BZERO */
  144.  
  145. //9.0 scanf 健壮的
  146. #ifndef SAFETY_SCANF
  147. #define SAFETY_SCANF(scanf_code,...) \
  148. while(printf(__VA_ARGS__),scanf_code){\
  149. while(getchar()!='\n');\
  150. puts("输入出错,请按照提示重新操作!");\
  151. }\
  152. while(getchar()!='\n')
  153. #endif /*!SAFETY_SCANF*/
  154.  
  155. //10.0 简单的time帮助宏
  156. #ifndef TIME_PRINT
  157. #define TIME_PRINT(code) {\
  158. clock_t __st,__et;\
  159. __st=clock();\
  160. code\
  161. __et=clock();\
  162. printf("当前代码块运行时间是:%lf秒\n",(0.0+__et-__st)/CLOCKS_PER_SEC);\
  163. }
  164. #endif /*!TIME_PRINT*/
  165.  
  166. //11.0 等待的宏 这里 已经处理好了
  167. #define _STR_PAUSEMSG "请按任意键继续. . ."
  168. extern void sh_pause(void);
  169. #ifndef INIT_PAUSE
  170.  
  171. # ifdef _DEBUG
  172. # define INIT_PAUSE() atexit(sh_pause)
  173. # else
  174. # define INIT_PAUSE() (void) /* 别说了,都重新开始吧 */
  175. # endif
  176.  
  177. #endif/* !INIT_PAUSE */
  178.  
  179. //12.0 判断是大端序还是小端序,大端序返回true
  180. extern bool sh_isbig(void);
  181.  
  182. /**
  183. * sh_free - 简单的释放内存函数,对free再封装了一下
  184. **可以避免野指针
  185. **pobj:指向待释放内存的指针(void*)
  186. **/
  187. extern void sh_free(void** pobj);
  188.  
  189. /**
  190. * 获取 当前时间串,并塞入tstr中长度并返回
  191. ** 使用举例
  192. char tstr[64];
  193. puts(gettimes(tstr, LEN(tstr)));
  194. **tstr : 保存最后生成的最后串
  195. **len : tstr数组的长度
  196. ** : 返回tstr首地址
  197. **/
  198. extern int sh_times(char tstr[], int len);
  199.  
  200. #endif/* ! _H_CHEAD */

主要是跨平台的一些帮助宏,开发中用到的通用宏.具体schead.c实现如下

  1. #include <schead.h>
  2.  
  3. //简单通用的等待函数
  4. void
  5. sh_pause(void)
  6. {
  7. rewind(stdin);
  8. printf(_STR_PAUSEMSG);
  9. getchar();
  10. }
  11.  
  12. //12.0 判断是大端序还是小端序,大端序返回true
  13. bool
  14. sh_isbig(void)
  15. {
  16. static union {
  17. unsigned short _s;
  18. unsigned char _cs[sizeof(unsigned short)];
  19. } __ut = { };
  20. return __ut._cs[] == ;
  21. }
  22.  
  23. /**
  24. * sh_free - 简单的释放内存函数,对free再封装了一下
  25. **可以避免野指针
  26. **@pobj:指向待释放内存的指针(void*)
  27. **/
  28. void
  29. sh_free(void** pobj)
  30. {
  31. if (pobj == NULL || *pobj == NULL)
  32. return;
  33. free(*pobj);
  34. *pobj = NULL;
  35. }
  36.  
  37. #if defined(_MSC_VER)
  38. /**
  39. * Linux sys/time.h 中获取时间函数在Windows上一种移植实现
  40. **tv : 返回结果包含秒数和微秒数
  41. **tz : 包含的时区,在window上这个变量没有用不返回
  42. ** : 默认返回0
  43. **/
  44. int
  45. gettimeofday(struct timeval* tv, void* tz)
  46. {
  47. time_t clock;
  48. struct tm tm;
  49. SYSTEMTIME wtm;
  50.  
  51. GetLocalTime(&wtm);
  52. tm.tm_year = wtm.wYear - ;
  53. tm.tm_mon = wtm.wMonth - ; //window的计数更好写
  54. tm.tm_mday = wtm.wDay;
  55. tm.tm_hour = wtm.wHour;
  56. tm.tm_min = wtm.wMinute;
  57. tm.tm_sec = wtm.wSecond;
  58. tm.tm_isdst = -; //不考虑夏令时
  59. clock = mktime(&tm);
  60. tv->tv_sec = (long)clock; //32位使用,接口已经老了
  61. tv->tv_usec = wtm.wMilliseconds * ;
  62.  
  63. return _RT_OK;
  64. }
  65. #endif
  66.  
  67. /**
  68. * 获取 当前时间串,并塞入tstr中C长度并返回
  69. ** 使用举例
  70. char tstr[64];
  71. puts(gettimes(tstr, LEN(tstr)));
  72. **tstr : 保存最后生成的最后串
  73. **len : tstr数组的长度
  74. ** : 返回tstr首地址
  75. **/
  76. int
  77. sh_times(char tstr[], int len)
  78. {
  79. struct tm st;
  80. time_t t = time(NULL);
  81. localtime_r(&t, &st);
  82. return (int)strftime(tstr, len, "%F %X", &st);
  83. }

后面是list.c的具体实现了

  1. #include <list.h>
  2.  
  3. /*
  4. * 采用头查法插入结点, 第一使用需要 list_t head = NULL;
  5. *返回 _RT_OK 表示成功!
  6. * ph : 指向头结点的指针
  7. * node : 待插入的结点对象
  8. */
  9. int
  10. list_add(list_t* ph, void* node)
  11. {
  12. if (ph == NULL || node == NULL){
  13. CERR("list_add 检查到(pal == NULL || node == NULL)!");
  14. return _RT_EP;
  15. }
  16.  
  17. list_next(node) = *ph;
  18. *ph = node;
  19.  
  20. return _RT_OK;
  21. }
  22.  
  23. /*
  24. * 链表中查找函数,查找失败返回NULL,查找成功直接返回那个结点,推荐不要乱改,否则就崩了.
  25. *如果需要改的话,推荐 用 list_findpop, 找到并弹出
  26. * h : 链表头结点
  27. * cmp : 查找的比较函数
  28. * left : cmp(left, right) 用的左结点
  29. * : 返回查找的结点对象
  30. */
  31. void*
  32. list_find(list_t h, icmp_f cmp, const void* left)
  33. {
  34. struct __lnode* head;
  35. if(cmp == NULL || left == NULL){
  36. CERR("list_find 检查到(cmp == NULL || left == NULL)!");
  37. return NULL;
  38. }
  39. //找到结果直接结束
  40. for(head = h; head; head = head->next)
  41. if(cmp(left, head) == )
  42. break;
  43. return head;
  44. }
  45.  
  46. /*
  47. * 查找到要的结点,并弹出,需要你自己回收
  48. * ph : 指向头结点的指针
  49. * cmp : 比较函数,将left同 *ph中对象按个比较
  50. * left : cmp(left, x) 比较返回 0 >0 <0
  51. * : 找到了退出/返回结点, 否则返回NULL
  52. */
  53. void*
  54. list_findpop(list_t *ph, icmp_f cmp, const void* left)
  55. {
  56. struct __lnode *head, *tmp;
  57. if((!ph) || (!cmp) || (!left) || !(head = *ph)){
  58. CERR("check find {(!ph) || (!cmp) || (!left) || !(head = *ph)}!");
  59. return NULL;
  60. }
  61. //头部检测
  62. if(cmp(left, head) == ){
  63. *ph = head->next;
  64. return head;
  65. }
  66. //后面就是普通的
  67. while((tmp = head->next)){
  68. if(cmp(left, tmp) == ){
  69. head->next = tmp->next;
  70. break;
  71. }
  72. head = tmp;
  73. }
  74.  
  75. return tmp; //仍然没有找见
  76. }
  77.  
  78. /*
  79. * 这里获取当前链表长度, 推荐调用一次就记住len
  80. * h : 当前链表的头结点
  81. * : 返回 链表长度 >=0
  82. */
  83. int list_len(list_t h)
  84. {
  85. int len = ;
  86. while(h){
  87. ++len;
  88. h = list_next(h);
  89. }
  90. return len;
  91. }
  92.  
  93. /*
  94. * 查找索引位置为idx的结点,找不见返回NULL
  95. * h : 当前结点
  96. * idx : 查找的索引值[0,len)
  97. * : 返回查到的结点,如果需要删除的推荐调用 list_pop(&h, idx);
  98. */
  99. void*
  100. list_get(list_t h, int idx)
  101. {
  102. if(h==NULL || idx < ){
  103. CERR("check is {h==NULL || idx < 0}");
  104. return NULL;
  105. }
  106. //主要查找函数,代码还是比较精简的还是值得学习的
  107. while(h){
  108. if(idx-- == )
  109. return h;
  110. h = list_next(h);
  111. }
  112.  
  113. if(idx > )
  114. CERR("check is idx >= length!, idx-length=%d.", idx);
  115. return NULL;
  116. }
  117.  
  118. /*
  119. * 按照索引弹出并返回结点, 需要自己回收这个结点 推荐 free(list_pop...);
  120. * ph : 指向链表结点的指针
  121. * idx : 弹出的索引
  122. * return : 无效的弹出,返回NULL
  123. */
  124. void*
  125. list_pop(list_t* ph, int idx)
  126. {
  127. struct __lnode *head, *front;//第一个是要找的结点,后面是它的前驱结点
  128. if((!ph) || (idx<) || !(head=*ph)){
  129. CERR("check is {(!ph) || (idx<0) || !(head=*ph)}");
  130. return NULL;
  131. }
  132.  
  133. for(front = NULL; head && idx>; --idx){
  134. front = head;
  135. head = head->next;
  136. --idx;
  137. }
  138.  
  139. if(idx>){
  140. CERR("check is idx>length, idx-length = %d.", idx);
  141. return NULL;
  142. }
  143. //下面就是找到的请况,返回结果
  144. if(front == NULL)
  145. *ph = head->next;
  146. else
  147. front->next = head->next;
  148. return head;
  149. }
  150.  
  151. /*
  152. * 返回结点node 的上一个结点,如果node = NULL, 返回最后一个结点
  153. * h : 当前链表结点
  154. * node : 待查找的结点信息
  155. * return : 返回查找到的结点,不存在返回NULL
  156. */
  157. void*
  158. list_front(list_t h, void* node)
  159. {
  160. struct __lnode* head = h; //直接跑到崩溃同strcpy
  161. while(head->next && head->next != node)
  162. head = head->next;
  163. return head->next == node ? head : NULL;
  164. }
  165.  
  166. /*
  167. * 和 list_add 功能相似,但是插入位置在尾巴那
  168. * ph : 待插入结点的指针
  169. * node : 待插入的当前结点
  170. */
  171. int
  172. list_addlast(list_t* ph, void* node)
  173. {
  174. struct __lnode* head;
  175. if(!ph || !node){
  176. CERR("check is {!ph || !node}! not nothing in it!");
  177. return _RT_EP;
  178. }
  179.  
  180. list_next(node) = NULL;//将这个结点的置空
  181. if(!(head=*ph)){ //插入的是头结点直接返回
  182. *ph = node;
  183. return _RT_OK;
  184. }
  185.  
  186. while(head->next)
  187. head = head->next;
  188. head->next = node;
  189. return _RT_OK;
  190. }
  191.  
  192. /*
  193. * 在链表的第idx索引处插入结点,也必须需要 list_t head = NULL; 在idx过大的时候
  194. *插入尾巴处,如果<0直接返回 _RT_EP. 成功了返回 _RT_OK
  195. * ph : 指向头结点的指针
  196. * idx : 结点的索引处
  197. * node : 待插入的结点
  198. */
  199. int
  200. list_addidx(list_t* ph, int idx, void* node)
  201. {
  202. struct __lnode* head;
  203. if(!ph || idx< || !node){ //以后可能加入 idx < 0的尾巴插入细则
  204. CERR("check is {!ph || idx<0 || !node}! Don't naughty again!");
  205. return _RT_EP;
  206. }
  207. //插入做为头结点
  208. if(!(head=*ph) || idx == ){
  209. list_next(node) = *ph;
  210. *ph = node;
  211. return _RT_OK;
  212. }
  213.  
  214. while(head->next && idx>){
  215. --idx;
  216. head = head->next;
  217. }
  218. list_next(node) = head->next;
  219. head->next = node;
  220. return _RT_OK;
  221. }
  222.  
  223. /*
  224. * 这里的销毁函数,只有这些数据都是栈上的才推荐这么做,会自动让其指向NULL
  225. * ph : 指向当前链表结点的指针
  226. */
  227. void
  228. list_destroy(list_t* ph)
  229. {
  230. struct __lnode *head, *next;
  231. if((!ph) || !(head=*ph))
  232. return;
  233. do{ //do 循环可以省略一次判断,但是有点丑陋
  234. next = head->next;
  235. free(head);
  236. }while((head=next));
  237.  
  238. *ph = NULL;
  239. }

关于list写的比较多,也有一点简单理解,上面虽然简陋,但是很精简,很指导不知道朋友学习使用,很通用的实在库. 到这里我们的一些都

这么随意的介绍完了.

  再次分享个人学习习惯,别人说的太多,还是不懂,直接让我看代码就可以了,每次都是对着代码敲明白了.当然老外的书说的很明白,不得不服.

一下就懂了. 每一个大功能都是一个个小模块组成了, 没经过坑坑洼洼, 自己都不相信自己可以. 不管怎么选择都很公平,需要是 用 高付出, 在第8号当铺典当

你想要的东西.

  共勉.希望的我的家人常快乐, 儿子在外对不住您们了, 目送飞云,一切安好!

后记

  错误是难免的,欢迎交流技术. 其实这个框架整体代码去年早就写好了, 后面有了点项目感悟,重新构建一下,提升性能,

就简单分享在这,值得和我一样菜的人学习交流. 设计很重要,但绝壁不是设计模式. 拜~,有机会 下次再分享感悟.

C 封装一个通用链表 和 一个简单字符串开发库的更多相关文章

  1. 封装一个通用递归算法,使用TreeIterator和TreeMap来简化你的开发工作。

    在实际工作中,你肯定会经常的对树进行遍历,并在树和集合之间相互转换,你会频繁的使用递归. 事实上,这些算法在逻辑上都是一样的,因此可以抽象出一个通用的算法来简化工作. 在这篇文章里,我向你介绍,我封装 ...

  2. 封装一个通用的PopupWindow

    上篇文章是关于建造者设计模式的,今天顺便封装一个通用的 PopupWindow 来实践一下, 同时也方便以后使用 PopupWindow,本文将从下面几个方面来介绍 PopupWindow 及其封装, ...

  3. C 构造一个 简单配置文件读取库

    前言 最近看到这篇文章, json引擎性能对比报告 http://www.oschina.net/news/61942/cpp-json-compare?utm_source=tuicool 感觉技术 ...

  4. 一步一步教你从零开始写C语言链表---构建一个链表

      版权声明:本文为博主原创文章,如有需要,请注明转载地址:http://blog.csdn.net/morixinguan.若是侵权用于商业用途,请联系博主,否则将追究责任 https://blog ...

  5. 【算法】C++用链表实现一个箱子排序附源代码详解

    01 箱子排序 1.1 什么是分配排序? 分配排序的基本思想:排序过程无须比较关键字,而是通过"分配"和"收集"过程来实现排序.它们的时间复杂度可达到线性阶:O ...

  6. 编写一个通用的Makefile文件

    1.1在这之前,我们需要了解程序的编译过程 a.预处理:检查语法错误,展开宏,包含头文件等 b.编译:*.c-->*.S c.汇编:*.S-->*.o d.链接:.o +库文件=*.exe ...

  7. Linux C编程学习之开发工具3---多文件项目管理、Makefile、一个通用的Makefile

    GNU Make简介 大型项目的开发过程中,往往会划分出若干个功能模块,这样可以保证软件的易维护性. 作为项目的组成部分,各个模块不可避免的存在各种联系,如果其中某个模块发生改动,那么其他的模块需要相 ...

  8. LeetCode 笔记系列六 Reverse Nodes in k-Group [学习如何逆转一个单链表]

    题目:Given a linked list, reverse the nodes of a linked list k at a time and return its modified list. ...

  9. 写一个ajax程序就是如此简单

    写一个ajax程序就是如此简单 ajax介绍: 1:AJAX全称为Asynchronous JavaScript and XML(异步JavaScript和XML),指一种创建交互式网页应用的网页开发 ...

随机推荐

  1. 【bzoj1076】[SCOI2008]奖励关 期望dp+状态压缩dp

    题目描述 你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关.在这个奖励关里,系统将依次随机抛出k次宝物,每次你都可以选择吃或者不吃(必须在抛出下一个宝物之前做出选择,且现在决定不吃的宝物以后也不能再 ...

  2. 《转》HTTP 协议入门

    HTTP 协议是互联网的基础协议,也是网页开发的必备知识,最新版本 HTTP/2 更是让它成为技术热点. 本文介绍 HTTP 协议的历史演变和设计思路. 一.HTTP/0.9 HTTP 是基于 TCP ...

  3. C# 大文件的复制方法

    如何复制读取大文件,也许困惑了很多人很长时间,这个不知道怎么搞,的确让人头疼欲裂,知道了你就才发现原来那么简单,话不多说,直入正题```` static void Main(string[] args ...

  4. 个人vim配置

    YouCompletMe支持golang cd .vim/bundle/YouCompleteMe ./install.sh --clang-completer --go-completer clan ...

  5. [HDU4532]湫秋系列故事——安排座位

    题面在这里 description 有\(n\)种颜色的小球,每种颜色的小球有\(a_i\)个: 要把它们摆成一排,求相邻小球颜色不相同的摆放方案数. 任意两个合理的安排方法,只要有一个位置的同学不同 ...

  6. 详解利用ELK搭建Docker容器化应用日志中心

    概述 应用一旦容器化以后,需要考虑的就是如何采集位于Docker容器中的应用程序的打印日志供运维分析.典型的比如SpringBoot应用的日志 收集.本文即将阐述如何利用ELK日志中心来收集容器化应用 ...

  7. Java实验报告(实验四)

    北京电子科技学院(BESTI) 实     验    报     告 课程:Java    班级:1352班      姓名:王国伊    学号:20135207 成绩:             指导 ...

  8. mac, xcode 6.1 安装command line tools 支持,autoconf,automake等

    以下软件包 都去我的环境库找到 1 先安装 tcl库 2 安装macports /opt/local/bin/port 一般装到这里 安装autoconf时提示: Warning: The Xcode ...

  9. SpringMVC源码解析-HTTP请求处理和分发

    1.HandlerMapping的配置和设计 在初始化完成时,所有的handlerMapping都已经被加载,handlerMapping存储着HTTP请求对应的映射数据,每一个handlerMapp ...

  10. SFM

    1.相机模型,内参数和外参数矩阵,相机标定: 2.极线约束和本征矩阵:特征点提取与匹配:提取到的特征点计算本征矩阵(五对以上的点)findEssentialMat(),需啊要点对,焦距参数,cx,cy ...