对云风 cstring 第二次解析
前言
????????
?
從明天起?關心糧食和蔬菜
我有一所房子?面朝大海?春暖花開
?
本文前提條件
1.了解?posix?線程
2.了解?原子操作
3.具備簡單C基礎,或者?你也敲一遍.
如果上面不太清楚,你可以翻看我以前的博客,或者'百度'搜索.
?
結論
1.云風前輩的?玩具?cstring?有點坑,?內存管理很隨意(也可能時我菜,理解不了他飄逸的寫法)
2.對于江湖中成名已久的?高手,?其實?勝在?思路上.
3.前輩3-4h搞完的,重構了1周,?發現?const?char*?和?char*?夠用了,真的,越簡單越針對 ,?越好,學習成本越低
對簡單開源代碼有興趣可以看看,畢竟開源的不都是好的.這里做的工作就是簡單和擴平臺,簡單可用升級,如果你也對C字符串感興趣
可以看看,否則沒有必要.
?
正文
到這里扯皮結束了,?最近任務有點多,游戲公司加班太瘋狂了,做的越快任務越多.哎.?以前博客可能講了不少關于cstring?結構設計.
這里就簡單扯一點,重構部分.?從整體上講,細節自己多練習了.
?
1.跨平臺所做的工作
跨平臺主要圍繞等待函數和原子操作封裝,看下面?的
sc_atom.h?文件內容
#ifndef _SC_ATOM #define _SC_ATOM/** 作者 : wz* * 描述 : 簡單的原子操作,目前只考慮 VS(CL) 小端機 和 gcc* 推薦用 posix 線程庫*/// 如果 是 VS 編譯器 #if defined(_MSC_VER)#include <Windows.h>//忽略 warning C4047: “==”:“void *”與“LONG”的間接級別不同 #pragma warning(disable:4047) // v 和 a 多 long 這樣數據 #define ATOM_FETCH_ADD(v, a) \InterlockedExchangeAdd((LONG*)&(v), (LONG)(a))#define ATOM_ADD_FETCH(v, a) \InterlockedAdd((LONG*)&(v), (LONG)(a))#define ATOM_SET(v, a) \InterlockedExchange((LONG*)&(v), (LONG)(a))#define ATOM_CMP(v, c, a) \(c == InterlockedCompareExchange((LONG*)&(v), (LONG)(a), (LONG)c))/*對于 InterlockedCompareExchange(v, c, a) 等價于下面long tmp = v ; v == a ? v = c : ; return tmp;咱么的 ATOM_FETCH_CMP(v, c, a) 等價于下面long tmp = v ; v == c ? v = a : ; return tmp;*/ #define ATOM_FETCH_CMP(v, c, a) \InterlockedCompareExchange((LONG*)&(v), (LONG)(a), (LONG)c)#define ATOM_LOCK(v) \while(ATOM_SET(v, 1)) \Sleep(0)#define ATOM_UNLOCK(v) \ATOM_SET(v, 0)//否則 如果是 gcc 編譯器 #elif defined(__GNUC__)#include <unistd.h>/*type tmp = v ; v += a ; return tmp ;type 可以是 8,16,32,84 的 int/uint*/ #define ATOM_FETCH_ADD(v, a) \__sync_fetch_add_add(&(v), (a))/*v += a ; return v;*/ #define ATOM_ADD_FETCH(v, a) \ __sync_add_and_fetch(&(v), (a))/*type tmp = v ; v = a; return tmp;*/ #define ATOM_SET(v, a) \__sync_lock_test_and_set(&(v), (a))/*bool b = v == c; b ? v=a : ; return b;*/ #define ATOM_CMP(v, c, a) \__sync_bool_compare_and_swap(&(v), (c), (a))/*type tmp = v ; v == c ? v = a : ; return v;*/ #define ATOM_FETCH_CMP(v, c, a) \__sync_val_compare_and_swap(&(v), (c), (a))/*加鎖等待,知道 ATOM_SET 返回合適的值_INT_USLEEP 是操作系統等待納秒數,可以優化,看具體操作系統使用方式int lock;ATOM_LOCK(lock);//to do think ...ATOM_UNLOCK(lock);*/ #define _INT_USLEEP (2) #define ATOM_LOCK(v) \while(ATOM_SET(v, 1)) \usleep(_INT_USLEEP)/*對ATOM_LOCK 解鎖, 當然 直接調用相當于 v = 0;*/ #define ATOM_UNLOCK(v) \__sync_lock_release(&(v))#endif /*!_MSC_VER && !__GNUC__ */#endif /*!_SC_ATOM*/
這里就是統一簡單包裝gcc?和?VS中提供的?gcc操作.
這里需要說明一下,?gcc?中 __sync__...?是基于編譯器層的?操作.?而?VS中Interlock...?是基于?Windows?api的
有很大不同,這里也只是簡單揉了一下,能用的相似的部分.例如
//忽略 warning C4047: “==”:“void *”與“LONG”的間接級別不同 #pragma warning(disable:4047) // v 和 a 多 long 這樣數據 #define ATOM_FETCH_ADD(v, a) \InterlockedExchangeAdd((LONG*)&(v), (LONG)(a))
主要是防止VS?警告和編譯器不通過而改的.?v類型不知道而?InterlockedExchangeAdd?只接受?LONG參數.
?
2.本土個人化接口文件定義
主要見sc_string.h?文件
#ifndef _H_SC_STRING #define _H_SC_STRING#include <stdint.h> #include <stddef.h> #include "sc_atom.h"#define _INT_STRING_PERMANENT (1) //標識 字符串是持久的相當于static #define _INT_STRING_INTERNING (2) //標識 字符串在運行時中,和內存同生死 #define _INT_STRING_ONSTACK (4) //標識 字符串分配在棧上//0 潛在 標識,這個字符串可以被回收,游離態#define _INT_INTERNING (32) //符號表 字符串大小 #define _INT_ONSTACK (128) //棧上內存大小struct cstring_data {char* cstr; //保存字符串的內容uint32_t hash; //字符串hash,如果是棧上的保存大小uint16_t type; //主要看 _INT_STRING_* 宏,默認0表示臨時串uint16_t ref; //引用的個數, 在 type == 0時候才有用 };typedef struct _cstring_buffer {struct cstring_data* str; } cstring_buffer[1]; //這個cstring_buffer是一個在棧上分配的的指針類型 typedef struct cstring_data* cstring; //給外部用的字符串類型/** v : 是一個變量名** 構建一個 分配在棧上的字符串.* 對于 cstring_buffer 臨時串,都需要用這個 宏聲明創建聲明,* 之后可以用 CSTRING_CLOSE 關閉和銷毀這個變量,防止這個變量變成臨時串*/ #define CSTRING_BUFFER(v) \char v##_cstring[_INT_ONSTACK] = { '\0' }; \struct cstring_data v##_cstring_data = { v##_cstring, 0, _INT_STRING_ONSTACK, 0 }; \cstring_buffer v; \v->str = &v##_cstring_data;/** v : CSTRING_BUFFER 聲明的字符串變量* 釋放字符串v,最好成對出現,創建和銷毀*/ #define CSTRING_CLOSE(v) \if(0 == (v)->str->type) \cstring_release((v)->str)/** s : cstring_buffer 類型* 方便直接訪問 struct cstring_data 變量*/ #define CSTRING(s) ((s)->str)/** v : 聲明的常量名,不需要雙引號* cstr : 常量字符串,必須是用 ""括起來的*/ #define CSTRING_LITERAL(v, cstr) \static cstring v; \if (NULL == v) { \cstring tmp = cstring_persist(""cstr, ( sizeof(cstr)/sizeof(char) - 1 )); \if(!ATOM_CMP(v, NULL, tmp)) { \cstring_free_persist(tmp); \} \}/* low level api, don't use directly */ cstring cstring_persist(const char* cstr, size_t sz); void cstring_free_persist(cstring s);/*public api*/ /** s : 待處理的串* return : 處理后永久串,可以返回或使用 * 主要將棧上的串拷貝到臨時堆上或者將臨時堆待釋放的串變到符號表中*/ extern cstring cstring_grab(cstring s);/** s : 待釋放的串* 主要是對臨時堆上的串進行引用計數刪除*/ extern void cstring_release(cstring s);/** sb : 字符串保存對象* str : 拼接的右邊字符串 * return : 返回拼接好的串 cstring*/ extern cstring cstring_cat(cstring_buffer sb, const char* str);/** sb : 字符串'池' , 這個字符串庫維護,你只管用* format : 格式化串,按照這個格式化輸出內容到 sb 中* ... : 可變參數內容* return : 格式化好的字符串,需要自己釋放** 后面 __attribute format 是在gcc上優化編譯行為,按照printf編譯約束來*/ extern cstring cstring_printf(cstring_buffer sb, const char* format, ...) #ifdef __GNUC____attribute__((format(printf, 2, 3))) #endif ;/** a : 字符串a* b : 字符串b* return : 當a和b不同是直接返回false,相同需要多次比較,相比strcmp 好一些*/ extern int cstring_equal(cstring a, cstring b);/** s : 字符串s* 為字符串s 生成hash值并返回,除了棧上的會設置上這個hash值*/ extern uint32_t cstring_hash(cstring s);// 臨時補得一個 日志宏,方便查錯,推薦這些接口 用日志系統代替,是一個整體 #ifndef cerr #include <stdio.h> /** 錯誤處理宏,msg必須是""括起來的字符串常量* __FILE__ : 文件全路徑* __func__ : 函數名* __LINE__ : 行數行* __VA_ARGS__ : 可變參數宏,* ##表示直接連接, 例如 a##b <=> ab*/ #define cerr(msg,...) \fprintf(stderr, "[%s:%s:%d]" msg "\n",__FILE__,__func__,__LINE__,##__VA_ARGS__) #endif#endif /*!_H_SC_STRING*/
以上是重構的所有接口,其實就是換皮了.外加了一些解釋.?后面添加了簡單測試宏.?以后在項目中換成內部日志系統.
?
3.接口文件實現
接口實現文件內容多一點
sc_string.c
?
#include "sc_string.h"#include <stdlib.h> #include <stdarg.h> #include <string.h>#define _INT_FORMAT_TEMP (1024) // 這樣做也是治標不治本,保存2k個字符串常量 #define _INT_INTERNING_POOL (2048) // hash size must be 2 pow #define _INT_HASH_START 8/** 字符串結點,可以認為是一個桶,鏈表* str : 字符串具體變量* buf : 臨時棧上變量,主要為 str.str 用的* next : 下一個字符串結點*/ struct string_node {struct cstring_data str;char buf[_INT_INTERNING];struct string_node* next; };/** 認為是字符串池,主要保存運行時段字符串變量,存在上限,因系統而定*/ struct string_pool {struct string_node node[_INT_INTERNING_POOL]; };/** 字符串對象的管理器* * lock : 加鎖用的* size : hash串的大小* hash : 串變量* total : 當前string_interning 中保存的字符串運行時常量* pool : 符號表存儲的地方* index : 標識pool 堆上保存到哪了*/ struct string_interning {int lock;int size;struct string_node** hash;int total;struct string_pool* pool;int index; };// 總的字符串管理對象實例化 static struct string_interning __sman = {0, _INT_HASH_START, NULL, 0, NULL, 0 };// 這個 sc_string.c 用到的加鎖解鎖簡化的 宏 #define LOCK() \ATOM_LOCK(__sman.lock)#define UNLOCK() \ATOM_UNLOCK(__sman.lock)/** 將字符串結點插入到hash表中** struct string_node** hash : 指向字符串鏈表結點指針的指針,認為是hash表* int sz : 新的hash表大小,上面指針的大小,這個值必須是 2的冪* struct string_node* n : 待插入hash表的結點*/ static void __insert_node(struct string_node** hash, int sz, struct string_node* n) {uint32_t h = n->str.hash;int idx = h & (sz - 1);n->next = hash[idx];hash[idx] = n; }/** 為 運行時的 字符串 struct string_interning 變量擴容,重新hash分配* * struct string_interning* si : 字符串池總對象*/ static void __expand(struct string_interning* si) {int nsize = si->size << 1; //簡單擴容struct string_node** nhash = calloc(nsize, sizeof(struct string_node*));if (NULL == nhash) {cerr("nhash calloc run error, memory insufficient.");exit(EXIT_FAILURE);}if (si->size > _INT_HASH_START) {for (int i = 0; i < si->size; ++i) {struct string_node* node = si->hash[i];while (node) { // 頭結點會變成尾結點struct string_node* tmp = node->next;__insert_node(nhash, nsize, node);node = tmp;}}}//釋放原先內存,重新回來free(si->hash);si->hash = nhash;si->size = nsize; }/** 創建一個運行時字符串對象并返回,理解為字符串常量.不需要釋放* * si : 總的字符串對象* cstr : 普通字符串量* sz : cstr需要的處理的長度,這個參數 必須 < _INT_INTERNING* hs : 這個字符串cstr的 hs值** : 返回值 是一個常量字符串的地址,有直接返回,沒有構建*/ static cstring __interning(struct string_interning* si, const char* cstr, size_t sz, uint32_t hs) {//si中hash表為NULL,保存無意義if (NULL == si->hash)return NULL;int sse = si->size;int idx = hs & (sse - 1);struct string_node* n = si->hash[idx];while (n) {if (n->str.hash == hs) if (strcmp(n->str.cstr, cstr) == 0) return &n->str;n = n->next;}// 這里采用的 jshash 函數不碰撞率 80% (4/5) , 這是經驗代碼if (si->total * 5 >= sse * 4)return NULL;if (NULL == si->pool) { //這個不是一個好設計.為了適應struct string_pool*,這種寫死的內存塊放在可以放在全局區,但是無法擴展// need not free poolsi->pool = malloc(sizeof(struct string_pool));if (NULL == si->pool) {cerr("si->pool malloc run error, memory insufficient.");exit(EXIT_FAILURE);}si->index = 0;}n = &si->pool->node[si->index++];memcpy(n->buf, cstr, sz);n->buf[sz] = '\0'; //cstr 最后是'\0' cstring cs = &n->str;cs->cstr = n->buf;cs->hash = hs;cs->type = _INT_STRING_INTERNING;cs->ref = 0;n->next = si->hash[idx];si->hash[idx] = n;return cs; }/** 生成一個字符串常量,主要放在 __sman.pool 中 ** cstr : 待處理的C字符串* sz : 字符串長度* hs : 字符串jshash的值* : 返回 生成的符號字符串的地址*/ static cstring __cstring_interning(const char* cstr, size_t sz, uint32_t hs) {cstring srt;LOCK();srt = __interning(&__sman, cstr, sz, hs);if (NULL == srt) {__expand(&__sman); //內存擴容srt = __interning(&__sman, cstr, sz, hs);}++__sman.total; //記錄當前字符串常量個數 UNLOCK();return srt; }/** jshash實現,當返回0設置為1,這里0用作特殊作用,表名初始化狀態* * buf : c字符串* len : 字符集長度* : 返回生成的字符串hash值*/ static uint32_t __get_hash(const char* buf, size_t len) {const uint8_t* ptr = (const uint8_t*)buf;size_t h = len; // hash初始化值size_t step = (len >> 5) + 1;for (size_t i = len; i >= step; i -= step)h ^= ((h<<5) + (h>>2) + ptr[i-1]); //將算法導論中東西直接用return h == 0 ? 1 : h; }/** 拷貝C字符串,并返回地址** cstr : c字符串* sz : cstr中處理的長度* : 返回當前字符串地址*/ static cstring __cstring_clone(const char* cstr, size_t sz) {if (sz < _INT_INTERNING)return __cstring_interning(cstr, sz, __get_hash(cstr, sz));//長的串,這里放在堆上struct cstring_data* p = malloc(sizeof(struct cstring_data) + sizeof(char) * (sz + 1));if(NULL == p){cerr("p malloc run error, memory insufficient.");exit(EXIT_FAILURE);}//ptr 指向后面為容納 cstr申請的內存,并初始化一些量void* ptr = p + 1;p->cstr = ptr;p->type = 0;p->ref = 1;memcpy(ptr, cstr, sz);((char*)ptr)[sz] = '\0';p->hash = 0;return p; }/* low level api, don't use directly */ cstring cstring_persist(const char* cstr, size_t sz) {cstring s = __cstring_clone(cstr, sz);if (0 == s->type) { //沒有放在運行時的常量中s->type = _INT_STRING_PERMANENT; // 標識持久的字符串中s->ref = 0;}return s; }void cstring_free_persist(cstring s) //用完釋放,這些api CSTRING_LITERAL宏中自動調用 {if (s->type == _INT_STRING_PERMANENT)free(s); }cstring cstring_grab(cstring s) {if (s->type & (_INT_STRING_PERMANENT | _INT_STRING_INTERNING))return s;if (s->type == _INT_STRING_ONSTACK)return __cstring_clone(s->cstr, s->hash);// 后面就是臨時串 type == 0if (0 == s->ref) //沒有引用讓其變為持久串,不說內存泄露了,就說已經釋放內存能不能用了都是問題s->type = _INT_STRING_PERMANENT;elseATOM_ADD_FETCH(s->ref, 1);return s; }void cstring_release(cstring s) {if (0 != s->type)return;if (0 == s->ref)return;ATOM_ADD_FETCH(s->ref, -1); //為了兼容 window特別處理if (s->ref == 0)free(s); }uint32_t cstring_hash(cstring s) {if (_INT_STRING_ONSTACK == s->type)return __get_hash(s->cstr, s->hash);if (0 == s->hash)s->hash = __get_hash(s->cstr, strlen(s->cstr));return s->hash; }int cstring_equal(cstring a, cstring b) {if (a == b)return 1;//都是運行時的字符串常量,肯定不同if (a->type == _INT_STRING_INTERNING && b->type == _INT_STRING_INTERNING)return 0;if (a->type == _INT_STRING_ONSTACK && b->type == _INT_STRING_ONSTACK) {if (a->hash != b->hash)return 0;return memcmp(a->cstr, b->cstr, a->hash) == 0;}uint32_t ha = cstring_hash(a);uint32_t hb = cstring_hash(b);if (ha != hb) //hash 能夠確認不同,但相同不一定同return 0;return strcmp(a->cstr, b->cstr) == 0; }/** 拼接c串a和b,可以話放在符號表中,大的話放在臨時區中** a : c串a* b : c串b* : 返回拼接后的cstring 變量*/ static cstring __cstring_cat(const char* a, const char* b) {size_t sa = strlen(a);size_t sb = strlen(b);size_t sm = sa + sb;if (sm < _INT_INTERNING) {char tmp[_INT_INTERNING];memcpy(tmp, a, sa);memcpy(tmp + sa, b, sb);tmp[sm] = '\0';return __cstring_interning(tmp, sm, __get_hash(tmp, sm));}//這里同樣走 堆上內存分配struct cstring_data* p = malloc(sizeof(struct cstring_data) + sizeof(char) * (sm + 1));if (NULL == p) {cerr("p malloc run error, memory insufficient.");exit(EXIT_FAILURE);}//ptr 指向后面為容納 cstr申請的內存,并初始化一些量char* ptr = (char*)(p + 1);p->cstr = ptr;p->type = 0;p->ref = 1;memcpy(ptr, a, sa);memcpy(ptr+sa, b, sb);ptr[sm] = '\0';p->hash = 0;return p; }cstring cstring_cat(cstring_buffer sb, const char* str) {cstring s = sb->str;if (s->type == _INT_STRING_ONSTACK) {int i = (int)s->hash;while (i < _INT_ONSTACK - 1) {s->cstr[i] = *str;if (*str == '\0') //可以就直接返回,全放在棧上return s;++s->hash;++str;++i;}s->cstr[i] = '\0';}// 棧上放不下,那就 試試 放在運行時中cstring tmp = s; sb->str = __cstring_cat(tmp->cstr, str); // 存在代碼冗余, _INT_ONSTACK > _INT_INTERNING cstring_release(tmp);return sb->str; }/** 根據模式化字符串,和可變參數拼接字符串,返回最終拼接的cstring 地址** format : 模板字符串* ap : 可變參數集* : 返回拼接后的字符串cstring變量*/ static cstring __cstring_format(const char* format, va_list ap) {static char* __cache = NULL; //持久化數據,編譯器維護char* rt;char* tmp = __cache;// read __cache buffer atomicif (tmp) {//tmp 獲取 __cache值, 如果 __cache == tmp ,會讓 __cache = NULLtmp = ATOM_FETCH_CMP(__cache, tmp, NULL);}if (NULL == tmp) {tmp = malloc(sizeof(char) * _INT_FORMAT_TEMP);if (NULL == tmp) {cerr("tmp malloc run error, memory insufficient.");exit(EXIT_FAILURE);}}int n = vsnprintf(tmp, _INT_FORMAT_TEMP, format, ap);if (n >= _INT_FORMAT_TEMP) {int sz = _INT_FORMAT_TEMP << 1;for (;;) {rt = malloc(sizeof(char)*sz);if (NULL == rt) {cerr("rt malloc run error, memory insufficient.");exit(EXIT_FAILURE);}n = vsnprintf(rt, sz, format, ap);if (n < sz)break;//重新開始,期待未來free(rt);sz <<= 1;}}else {rt = tmp;}cstring r = malloc(sizeof(struct cstring_data) + (n+1)*sizeof(char));if (NULL == r) {cerr("r malloc run error, memory insufficient.");exit(EXIT_FAILURE);}r->cstr = (char*)(r + 1);r->type = 0;r->ref = 1;r->hash = 0;memcpy(r->cstr, rt, n+1);// tmp != rt 時候, rt 構建臨時區為 臨時的if (tmp != rt) free(rt);//save tmp atomicif (!ATOM_CMP(__cache, NULL, tmp))free(tmp);return r; }cstring cstring_printf(cstring_buffer sb, const char* format, ...) {cstring s = sb->str;va_list ap;va_start(ap, format);if (s->type == _INT_STRING_ONSTACK) {int n = vsnprintf(s->cstr, _INT_ONSTACK, format, ap);if (n >= _INT_ONSTACK) {s = __cstring_format(format, ap);sb->str = s;}elses->hash = n;}else {cstring_release(sb->str);s = __cstring_format(format, ap);sb->str = s;}va_end(ap);return s; }
到這里基本結構就完成了.?簡單說一下,當我寫到下面這塊
?
void cstring_free_persist(cstring s) //用完釋放,這些api CSTRING_LITERAL宏中自動調用 {if (s->type == _INT_STRING_PERMANENT)free(s); }cstring cstring_grab(cstring s) {if (s->type & (_INT_STRING_PERMANENT | _INT_STRING_INTERNING))return s;if (s->type == _INT_STRING_ONSTACK)return __cstring_clone(s->cstr, s->hash);// 后面就是臨時串 type == 0if (0 == s->ref) //沒有引用讓其變為持久串,不說內存泄露了,就說已經釋放內存能不能用了都是問題s->type = _INT_STRING_PERMANENT;elseATOM_ADD_FETCH(s->ref, 1);return s; }void cstring_release(cstring s) {if (0 != s->type)return;if (0 == s->ref)return;ATOM_ADD_FETCH(s->ref, -1); //為了兼容 window特別處理if (s->ref == 0)free(s); }
補充說明一下,這里??ATOM_ADD_FETCH?返回的是 %hu?的零,?但是?if ((hu)0 == -1)卻不等,這是?數據格式默認變成LONG比較的結果.
所以先進行原子操作,再去處理數據.?屬于一個隱含的知識點.
擴展一下,?當我們用VS2015?或者說Microsoft?系列IDE寫C程序,都是偽C代碼,走的是C++編譯器的extern "C"?部分.?比較惡心.
對于VS?DEBUG?模式下檢測內存?的方式是,?在你申請內存時候額外添加空間,free時候回檢測,這也就是他檢測內存異常而定手段.
具體見
// Tests the array of size bytes starting at first. Returns true if all of the // bytes in the array have the given value; returns false otherwise. static bool __cdecl check_bytes(unsigned char const* const first,unsigned char const value,size_t const size) throw() {unsigned char const* const last{first + size};for (unsigned char const* it{first}; it != last; ++it){if (*it != value)return false;}return true; }
這里再擴展一下,自己的多個IDE編程感受,?用gcc的時候你需要小心翼翼,明白很多細節,否則?直接跪了.?而用VS開發,很大方去你媽,不懂沒關系
就是亂寫,編譯調試都不用太關心,省了1半開發調試時間.只高不低,生產力提升了.技術下降了.真希望Linux?上有個可視化的VS.
到這里擴展結束,繼續說一下,它坑的地方
特別是對于?cstring_grab?中 0 ==?s->ref?的時候,?這時候?s?是一個被釋放的臨時串.?這樣改個類型就直接返回了,相當于
使用已經釋放的內存,多恐怖.
就是到這里,?感覺這個玩具已經扶不起來,例如
cstring_cat => cstring_release =>cstring_grab?這種程序崩了.如下
// 測試內存混亂puts("\n--新的測試開始--\n");CSTRING_BUFFER(cu);cstring ks = cstring_cat(cu, "你好111111111111111111111111111111111111111111111111111111111111111111111111111111""好的11111111111111111111111111111111111111111111111111111111111111111111111111111111111""坑啊22222222222222222222222222222222222222222222222222222222222222222222222222222222222""你能力比我強,強改只會走火入魔,坑""1111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222");printf("type:%u, ref:%u, cstr:%s\n",ks->type, ks->ref, ks->cstr);CSTRING_CLOSE(cu);//這里繼續使用這個串cstring bks = cstring_grab(ks); // 它也沒有起死回生的能力,代碼崩掉printf("type:%u, ref:%u, cstr:%s\n", bks->type, bks->ref, bks->cstr);
代碼一執行,程序就崩了.
不想改了,?強改比自己能力強的?設計問題 容易引火焚身.
大家注意一下,有好想法,?可以試試,改好了分享.?我的感覺內存?管理方式隱含的太多了.?有點亂.絕逼內存泄露,畢竟讓別人用.
?
4.運行實例
首先看原始的測試demo
test.c
#include "sc_string.h"#include <stdio.h>static cstring __foo(cstring t) {CSTRING_LITERAL(hello, "hello");CSTRING_BUFFER(ret);if (cstring_equal(hello, t))cstring_cat(ret, "equal");else cstring_cat(ret, "not equal");return cstring_grab(CSTRING(ret)); }static void __test() {CSTRING_BUFFER(a);cstring_printf(a, "%s", "hello");cstring b = __foo(CSTRING(a));printf("%s\n", b->cstr);cstring_printf(a, "very long string %01024d", 0);printf("%s\n", CSTRING(a)->cstr);CSTRING_CLOSE(a);cstring_release(b); }int main(void) {__test();#ifdef _MSC_VER system("pause"); #endif // !_MSC_VERreturn 0; }
window?運行結果
?
到這里window?上基本都跑起來,?現在我們在gcc上測試一下.?首先需要將這些文件上傳到Linux服務器上,上傳之前統一用utf-8編碼保存.
?上面是Linux?跑的結果,?其中Makefile?文件內容如下
test.out : test.c sc_string.cgcc -g -Wall -march=native -o $@ $^
到這里?這個高級玩具要告一段落.?還有好多坑,這里就沒說了.?例如?cstring_cat?cstring_printf?這樣分配太慢了,?搞一次不行又重頭搞一次,?前面都是無用功.
但作為玩具已經夠炫了.期待云風前輩重構成?實戰級別的?c字符串,?反正我進過這次教訓,覺得C中?char*,const?char*,?const?char *?const?夠用了.
?
后記
大家有機會可以去cloudwn ?githup?上?下載?cstring-master?玩玩,?感受一下別人的代碼習慣和風格和設計思路.
有機會下次分享?實戰中的簡單日志庫.?歡迎吐槽,因為技術很菜總有不懂地方和錯誤的地方.
?
轉載于:https://www.cnblogs.com/life2refuel/p/5095586.html
總結
以上是生活随笔為你收集整理的对云风 cstring 第二次解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jQuery中$(function(){
- 下一篇: 化肥多少钱啊?