引言
这里需要分享的是一个 简单字符串库和 链表的基库,代码也许用到特定技巧.有时候回想一下,
如果我读书的时候有人告诉我这些关于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 特殊服务业 电子设备. 如果你穷你真的需要 认真学习编程,不要太沉迷于框架的学习中.
真的 人穷就应该多编程, 别人抢红包,你 需要写代码, , ,
这篇博文分享的框架后面都加了一点内容, 也简单补充一下. 内容很多基本都是垃圾. 首先 以一个 不区分大小写的函数压马路.
/* * 这是个不区分大小写的比较函数 * ls : 左边比较字符串 * rs : 右边比较字符串 * : 返回 ls>rs => >0 ; ls = rs => 0 ; ls<rs => <0 */ extern int str_icmp(const char* ls, const char* rs);
构造如下, 看完这里基本就可以关闭,毕竟后面更啰嗦!
/* * 这是个不区分大小写的比较函数 * ls : 左边比较字符串 * rs : 右边比较字符串 * : 返回 ls>rs => >0 ; ls = rs => 0 ; ls<rs => <0 */ int str_icmp(const char* ls, const char* rs) { int l, r; if(!ls || !rs) return (int)ls - (int)rs; do { if((l=*ls++)>='a' && l<='z') l -= 'a' - 'A'; if((r=*rs++)>='a' && r<='z') r -= 'a' - 'A'; } while(l && l==r); return l-r; }
到这里 基本上就值了. 学到上面函数 也算温故C 基础吧! O(∩_∩)O哈哈~
前言
终于到这里了,扯的有点多. 首先来看一下今天主要写的通用链表的接口,看设计
#ifndef _H_LIST #define _H_LIST #include <schead.h> /* * 这个万能单链表库 前提所有结点都是堆上分配的,设计的比较老了,能用 *注意 * 1.使用的时候,需要加上 _LIST_HEAD; 宏 * 2.创建的第一句话就是 list head = NULL; 开始从空链表开始list的生涯 */ struct __lnode { struct __lnode* next; }; // 不多说了一定放在想使用链表结构的结构体头部 #define _LIST_HEAD struct __lnode __ln; // 简单链表结构, 当你使用这个链表的时候 需要 list_t head = NULL; 开始使用之旅 typedef void* list_t; /* * 采用头查法插入结点, 第一使用需要 list_t head = NULL; *返回 _RT_OK 表示成功! * ph : 指向头结点的指针 * node : 待插入的结点对象 */ extern int list_add(list_t* ph, void* node); /* * 链表中查找函数,查找失败返回NULL,查找成功直接返回那个结点,推荐不要乱改,否则就崩了. *如果需要改的话,推荐 用 list_findpop, 找到并弹出 * h : 链表头结点 * cmp : 查找的比较函数 * left : cmp(left, right) 用的左结点 * : 返回查找的结点对象 */ extern void* list_find(list_t h, icmp_f cmp, const void* left); /* * 查找到要的结点,并弹出,需要你自己回收 * ph : 指向头结点的指针 * cmp : 比较函数,将left同 *ph中对象按个比较 * left : cmp(left, x) 比较返回 0 >0 <0 * : 找到了退出/返回结点, 否则返回NULL */ extern void* list_findpop(list_t *ph, icmp_f cmp, const void* left); /* * 这里获取当前链表长度, 推荐调用一次就记住len * h : 当前链表的头结点 * : 返回 链表长度 >=0 */ extern int list_len(list_t h); /* * 查找索引位置为idx的结点,找不见返回NULL * h : 当前结点 * idx : 查找的索引值[0,len) * : 返回查到的结点,如果需要删除的推荐调用 list_pop(&h, idx); */ extern void* list_get(list_t h, int idx); /* * 按照索引弹出并返回结点, 需要自己回收这个结点 推荐 free(list_pop...); * ph : 指向链表结点的指针 * idx : 弹出的索引 * return : 无效的弹出,返回NULL */ void* list_pop(list_t* ph, int idx); /* * 返回结点node 的上一个结点,如果node = NULL, 返回最后一个结点 * h : 当前链表结点 * node : 待查找的结点信息 * return : 返回查找到的结点,不存在返回NULL */ void* list_front(list_t h, void* node); /* * 这个宏推荐不使用, 主要返回结点n的下一个结点 * 第一种使用方法 node->next = (void*)list_node(n), 另一种是 list_node(n) = node; * n : 当前结点 */ #define list_next(n) (((struct __lnode*)n)->next) /* * 和 list_add 功能相似,但是插入位置在尾巴那 * ph : 待插入结点的指针 * node : 待插入的当前结点 */ int list_addlast(list_t* ph, void* node); /* * 在链表的第idx索引处插入结点,也必须需要 list_t head = NULL; 在idx过大的时候 *插入尾巴处,如果<0直接返回 _RT_EP. 成功了返回 _RT_OK * ph : 指向头结点的指针 * idx : 结点的索引处 * node : 待插入的结点 */ int list_addidx(list_t* ph, int idx, void* node); /* * 这里的销毁函数,只有这些数据都是栈上的才推荐这么做,会自动让其指向NULL * ph : 指向当前链表结点的指针 */ void list_destroy(list_t* ph); #endif // !_H_LIST
这里接口使用的 extern声明的希望外部直接使用, 没有extern的外部可以使用,属于扩展功能.
对于上面接口 简单的测试 代码如下
#include <list.h> struct lint { _LIST_HEAD; int node; }; //简单创建函数 static struct lint* __lint_new(int node) { struct lint* ln = malloc(sizeof(struct lint)); if(ln){ ln->node = node; } return ln; } //简单打印函数 static void __lint_puts(list_t head) { int len = list_len(head); int i; printf("当前链表中数据结果如下:"); for(i=0; i<len; ++i){ struct lint* tl = list_get(head, i); printf("%d ", tl->node); } putchar(' '); } /* * 这里简单测试一下 关于链表的常用接口 */ int main(int argc, char* argv[]) { list_t head = NULL; int arrs[] = { 1, 2, 3, 4, 5, 6, 7, 8}; int i; //这里添加结点 for(i=0; i<sizeof(arrs)/sizeof(*arrs); ++i) list_add(&head, __lint_new(arrs[i])); __lint_puts(head); //这里删除一个结点 free(list_pop(&head, 0)); __lint_puts(head); //删除第二个结点 free(list_pop(&head, 1)); __lint_puts(head); list_destroy(&head); return 0; }
测试了几个简答接口. 注释比较详细, 写的也比较简单相对于Linux内核的数据结构而言. 这里是个开门红.
临摹几遍都能理解C接口的简单设计.
正文
其实呀上面代码主要突出一个设计, 实现而言还是比较容易,因为结构有了,算法就能够写好了. 例如 获取某个结点的源码
/* * 这里获取当前链表长度, 推荐调用一次就记住len * h : 当前链表的头结点 * : 返回 链表长度 >=0 */ int list_len(list_t h) { int len = 0; while(h){ ++len; h = list_next(h); } return len; }
很基础也容易理解, 大多数代码其实结构设计好实现也就是时间问题, 也等同于业务了. 精妙的东西没有那么多, 魔鬼藏在细节里.向那些这个池那个组,都名次解释.
很普通.现在我们只谈设计, 最后会给出完整的代码. 同样还有一种结构, (状态不好,加班太多了,写的很水望见谅,因为很多东西说出来还是做不出来,做出来说的不好.)
看下面关于简单字符串设计代码
#ifndef _H_TSTRING #define _H_TSTRING #include <schead.h> //------------------------------------------------简单字符串辅助操作---------------------------------- /* * 主要采用jshash 返回计算后的hash值 * 不冲突率在 80% 左右还可以, 不要传入NULL */ extern unsigned str_hash(const char* str); //------------------------------------------------简单文本字符串辅助操作---------------------------------- #ifndef _STRUCT_TSTRING #define _STRUCT_TSTRING //简单字符串结构,并定义文本字符串类型tstring struct tstring { char* str; //字符串实际保存的内容 int len; //当前字符串大小 int size; //字符池大小 }; typedef struct tstring* tstring; #endif // !_STRUCT_TSTRING //文本串栈上创建内容,不想用那些技巧了,就这样吧 #define TSTRING_CREATE(var) struct tstring var = { NULL, 0, 0} #define TSTRING_DESTROY(var) free(var.str) /* * tstring 的创建函数, 会根据str创建一个 tstring结构的字符串 * * str : 待创建的字符串 * * ret : 返回创建好的字符串,如果创建失败返回NULL */ extern tstring tstring_create(const char* str); /* * tstring 完全销毁函数 * tstr : 指向tsting字符串指针量的指针 */ extern void tstring_destroy(tstring* tstr); /* * 向简单文本字符串tstr中添加 一个字符c * tstr : 简单字符串对象 * c : 待添加的字符 * ret : 返回状态码 见 schead 中 _RT_EB 码等 */ extern int tstring_append(tstring tstr, int c); /* * 向简单文本串中添加只读字符串 * tstr : 文本串 * str : 待添加的素材串 * ret : 返回状态码主要是 _RT_EP _RT_EM */ extern int tstring_appends(tstring tstr, const char* str); //------------------------------------------------简单文件辅助操作---------------------------------- /* * 简单的文件帮助类,会读取完毕这个文件内容返回,失败返回NULL. * 需要事后使用 tstring_destroy(&ret); 销毁这个字符串对象 * path : 文件路径 * ret : 返回创建好的字符串内容,返回NULL表示读取失败 */ extern tstring file_malloc_readend(const char* path); /* * 文件写入,没有好说的,会返回 _RT_EP _RT_EM _RT_OK * path : 文件路径 * str : 待写入的字符串 * ret : 返回写入的结果 */ extern int file_writes(const char* path, const char* str); /* * 文件追加内容, 添加str内同 * path : 文件路径 * str : 待追加的文件内同 * : 返回值,主要是 _RT_EP _RT_EM _RT_OK 这些状态 */ extern int file_append(const char* path, const char* str); #endif // !_H_TSTRING
这个串可以用在读取一个大串,主要解决的问题是内存空间分配问题,还可以用.最大浪费就50%.
现在我们简单说一下具体实现,其实一看
#ifndef _STRUCT_TSTRING #define _STRUCT_TSTRING //简单字符串结构,并定义文本字符串类型tstring struct tstring { char* str; //字符串实际保存的内容 int len; //当前字符串大小 int size; //字符池大小 }; typedef struct tstring* tstring; #endif // !_STRUCT_TSTRING
全部明白了. 就是 len表现当前str中保存的长度, size表现当前str的容量.分配代码如下
//简单分配函数,智力一定会分配内存的, len > size的时候调用这个函数 static int __tstring_realloc(tstring tstr, int len) { int size = tstr->size; for (size = size < _INT_TSTRING ? _INT_TSTRING : size; size < len; size <<= 1) ; //分配内存 char *nstr = realloc(tstr->str, size); if (NULL == nstr) { SL_NOTICE("realloc(tstr->str:0x%p, size:%d) is error!", tstr->str, size); return _RT_EM; } tstr->str = nstr; tstr->size = size; return _RT_OK; }
len是新的str大小.后面展现 全部的演示代码.
#include <tstring.h> #include <sclog.h> /* * 主要采用jshash 返回计算后的hash值 * 不冲突率在 80% 左右还可以, 不要传入NULL */ unsigned str_hash(const char* str) { size_t i, h = strlen(str), sp = (h >> 5) + 1; unsigned char* ptr = (unsigned char*)str; for (i = h; i >= sp; i -= sp) h ^= ((h<<5) + (h>>2) + ptr[i-1]); return h ? h : 1; } /* * tstring 的创建函数, 会根据str创建一个 tstring结构的字符串 * * str : 待创建的字符串 * * ret : 返回创建好的字符串,如果创建失败返回NULL */ tstring tstring_create(const char* str) { tstring tstr = calloc(1, sizeof(struct tstring)); if (NULL == tstr) { SL_NOTICE("calloc is sizeof struct tstring error!"); return NULL; } tstring_appends(tstr, str); return tstr; } /* * tstring 完全销毁函数 * tstr : 指向tsting字符串指针量的指针 */ void tstring_destroy(tstring* tstr) { if (tstr && *tstr) { //展现内容 free((*tstr)->str); free(*tstr); *tstr = NULL; } } //文本字符串创建的度量值 #define _INT_TSTRING (32) //简单分配函数,智力一定会分配内存的, len > size的时候调用这个函数 static int __tstring_realloc(tstring tstr, int len) { int size = tstr->size; for (size = size < _INT_TSTRING ? _INT_TSTRING : size; size < len; size <<= 1) ; //分配内存 char *nstr = realloc(tstr->str, size); if (NULL == nstr) { SL_NOTICE("realloc(tstr->str:0x%p, size:%d) is error!", tstr->str, size); return _RT_EM; } tstr->str = nstr; tstr->size = size; return _RT_OK; } /* * 向简单文本字符串tstr中添加 一个字符c * tstr : 简单字符串对象 * c : 待添加的字符 * ret : 返回状态码 见 schead 中 _RT_EM 码等 */ int tstring_append(tstring tstr, int c) { //不做安全检查 int len = tstr->len + 2; // c + ' ' 而len只指向 字符串strlen长度 //需要进行内存分配,唯一损失 if ((len > tstr->size) && (_RT_EM == __tstring_realloc(tstr, len))) return _RT_EM; tstr->len = --len; tstr->str[len - 1] = c; tstr->str[len] = '