雲風(cloudwu)開源的 cstring 是一個極精簡卻工業級的 C 字符串庫。本文從頭文件宏設計、三類字符串生命週期、原子操作應用、內存佈局技巧、懸而未決的擴容缺陷等五個維度深度剖析。
| 類型宏 | 值 | 存儲區 | 生命週期 | 引用計數 |
|---|---|---|---|---|
CSTRING_PERMANENT | 1 | 堆 | 進程全程,手動釋放 | 無 (ref=0) |
CSTRING_INTERNING | 2 | 全域符號表池 | 進程全程,去重共享 | 無 (ref=0) |
CSTRING_ONSTACK | 4 | 棧 (宏展開局部陣列) | 函數返回銷毀 | 無 |
| 普通運行時字符串 | 0 | 堆 | 引用計數管理 | ref ≥ 1 |
CSTRING_BUFFER(var):棧上緩衝區一鍵建立#define CSTRING_BUFFER(var) \
char var##_cstring[CSTRING_STACK_SIZE] = { '\0' }; \
struct cstring_data var##_cstring_data = { var##_cstring, 0, CSTRING_ONSTACK, 0 }; \
cstring_buffer var; \
var->str = &var##_cstring_data;
## 符號拼接:var=abc → abc_cstringcstring_buffer 定義為 struct { cstring_data* str; } cstring_buffer[1],陣列退化為指針,棧上分配卻可當指針傳遞CSTRING_CLOSE(var)(僅釋放類型 0 的字符串)CSTRING_LITERAL(var, "literal"):線程安全的全域常量#define CSTRING_LITERAL(var, cstr) \
static cstring var = NULL; \
if (var) {} else { \
cstring tmp = cstring_persist(""cstr, (sizeof(cstr)/sizeof(char))-1); \
if (!__sync_bool_compare_and_swap(&var, NULL, tmp)) \
cstring_free_persist(tmp); \
}
""cstr 編譯期拼接字面量,參數必須是字面量__sync_bool_compare_and_swap GCC 內建原子 CAS,無鎖線程安全初始化struct cstring_data *p = malloc(sizeof(struct cstring_data) + sz + 1); void *ptr = (void *)(p + 1); // p+1 跳過結構體,指向緊隨其後的內存 p->cstr = ptr; memcpy(ptr, cstr, sz); ((char*)ptr)[sz] = '\0';
一次 malloc 同時分配控制塊 + 數據區,提高緩存局部性、減少分配開銷。更極致的寫法用 C99 柔性陣列成員:
struct cstring_data {
uint32_t hash_size;
uint16_t type, ref;
char cstr[]; // 柔性陣列,不佔結構體大小
};
但作者保留 char *cstr 指針形式,為了支援棧上 CSTRING_BUFFER(需指向外部固定陣列)。
string_node:含 cstring_data、buffer[32]、next 指針string_pool:固定 1024 個 node 的陣列string_interning:hash 桶指針陣列、pool 指針、index 分配游標、total 計數total * 5 >= size * 4 (負載因子 0.8)n = &si->pool->node[si->index++]; // index 只增不減 // 超過 1024 次 interning 直接越界崩潰!
作者在代碼註釋 // need not free pool 表明不打算釋放,但固定 1024 上限極其危險,長期運行服務必崩。修復方案:pool 改為鏈表或動態陣列,index 改為自由鏈表分配器。
LOCK()/UNLOCK() 用 __sync_lock_test_and_set 實現自旋鎖static inline void LOCK() 中含 while 迴圈,GCC 不會內聯含迴圈函數,實際退化為普通函數調用__attribute__((always_inline)) 強制內聯,或直接用 pthread_spinlockstruct cstring_data *p = malloc(...); assert(p); // NDEBUG 開啟時 assert 消失,malloc 失敗直接解引用 NULL 崩潰
生產環境必須用 if (!p) { log_error; return NULL; } 替代 assert。
cstring 是「教科書級玩具」:展示了字符串分類、原子操作、柔性陣列、哈希去重等硬核技巧,但符號表固定上限、assert 誤用、inline 失效 等問題使其不適合直接用於生產核心路徑。適合作為學習範例,生產建議二次重構或使用成熟庫(如 sds、jemalloc 內建字符串)。