雲風(云风)的 cstring 是一個極具特色的 C 語言字符串庫,採用符號表(String Interning)+ 引用計數 + 原子操作 實現高效字符串共享與管理。本文記錄對其第二次深度解析、跨平台重構過程、發現的設計缺陷及最終感悟。
const char* + char* 才是工程落地的最優解,簡單即可靠。文件 sc_atom.h 統一封裝 GCC(__sync_* 內建函數)與 MSVC(Interlocked* Windows API)的原子操作:
// 原子加並獲取舊值
#if defined(_MSC_VER)
#define ATOM_FETCH_ADD(v, a) InterlockedExchangeAdd((LONG*)&(v), (LONG)(a))
#elif defined(__GNUC__)
#define ATOM_FETCH_ADD(v, a) __sync_fetch_and_add(&(v), (a))
#endif
// 原子比較交換(CAS)
#define ATOM_CMP(v, c, a) ... // 兩平台語義對齊
// 自旋鎖
#define ATOM_LOCK(v) while(ATOM_SET(v, 1)) Sleep/usleep(...)
#define ATOM_UNLOCK(v) ATOM_SET(v, 0)
關鍵點:MSVC 需強制轉型 (LONG*) 並禁用警告 C4047;GCC 內建函數無類型限制,直接操作內存地址。
struct cstring_data {
char *cstr; // 字符串內容指針
uint32_t hash; // hash 值(棧上字符串存長度)
uint16_t type; // 類型標識:持久/駐留/棧上/臨時
uint16_t ref; // 引用計數(僅臨時串有效)
};
#define _INT_STRING_PERMANENT 1 // 靜態持久串
#define _INT_STRING_INTERNING 2 // 運行時駐留串(符號表)
#define _INT_STRING_ONSTACK 4 // 棧上緩衝區串
// type == 0 表示臨時堆串,需引用計數管理
hash & (size-1) 取模。total*5 >= size*4)觸發 2 倍擴容,重新哈希所有節點。string_pool)預分配固定大小數組(2048),節點內含緩衝區(_INT_INTERNING=32 字節),超長串走堆分配。string_interning 實例採用自旋鎖(ATOM_LOCK)保護。| 接口 | 語義 |
|---|---|
cstring_grab(s) | 獲取字符串所有權:棧上串→拷貝到臨時/駐留;臨時串→引用計數+1;持久/駐留串→直接返回 |
cstring_release(s) | 釋放所有權:僅臨時串引用計數-1,歸零時 free |
cstring_cat(sb, str) | 拼接:棧上有空間則原地拼接,否則分配新串並釋放舊串 |
cstring_printf(sb, fmt, ...) | 格式化:棧上足夠則原地,否則堆分配,使用線程安全緩衝區緩存 |
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 == 0
if (0 == s->ref) { // 【缺陷點】
s->type = _INT_STRING_PERMANENT; // 已釋放內存卻標記為持久!
} else {
ATOM_ADD_FETCH(s->ref, 1);
}
return s;
}
崩潰場景:
cstring_cat 創建臨時串 → cstring_release 釋放內存(ref 歸零 free)。cstring_grab 獲取同一指針 → ref==0 進入分支,修改 type 為持久並返回已釋放的內存指針。測試代碼驗證崩潰:
CSTRING_BUFFER(cu);
cstring ks = cstring_cat(cu, "超長字符串..."); // 棧上放不下,走堆分配臨時串
CSTRING_CLOSE(cu); // 釋放臨時串內存
cstring bks = cstring_grab(ks); // 崩潰:訪問已釋放內存
InterlockedCompareExchange 參數順序與 GCC __sync_val_compare_and_swap 不同,宏定義時必須統一語義。std::string/std::string_view;C 用 const char* + 明確所有權約定,或成熟庫(sds、bstrlib)。free 時校驗,能捕獲越界/雙重釋放,但掩蓋了 Release 下的 UB。雲風 cstring 展示了符號表、原子操作、內存池等高級技術在 C 中的精妙組合,但內存管理邏輯存在硬傷,不適合直接用於生產環境。重構過程深化了對無鎖數據結構、字符串駐留、跨平台原子封裝的理解。最終認同:簡單、顯式、標準化的方案才是長久之計。源碼可在 cloudwu/cstring 參考學習。