用C實現動態擴容的string
導讀 |
眾所周知,C++ 中的string使用比較方便,關於C++ 中的string原始碼實現可以看我的這篇文章:原始碼分析C++的string的實現
|
眾所周知,C++ 中的string使用比較方便,關於C++ 中的string原始碼實現可以看我的這篇文章:原始碼分析C++的string的實現
最近工作中使用C語言,但又苦於沒有高效的字串實現,字串的拼接和裁剪都比較麻煩,而且每個字串都需要申請記憶體,記憶體的申請和釋放也很容易出bug,怎麼高效的實現一個不需要處理記憶體問題並且可以動態擴容進行拼接和裁剪的string呢?
一個好的string應該有以下功能?
- 建立字串
- 刪除字串
- 尾部追加字串
- 頭部插入字串
- 從尾部刪除N個字元
- 從頭部刪除N個字元
- 裁剪字串
- 獲取字串長度
- 獲取完整字串
下面來看看各個功能的實現:
首先定義一個string的控制程式碼,相當於C++中的例項
struct c_string; typedef struct c_string c_string_t;
在內部string的實現如下:
// string的初始記憶體大小 static const size_t c_string_min_size = 32; struct c_string { char *str; // 字串指標 size_t alloced; // 已分配的記憶體大小 size_t len; // 字串的實際長度 };
建立字串:
c_string_t *c_string_create(void) { c_string_t *cs; cs = calloc(1, sizeof(*cs)); cs->str = malloc(c_string_min_size); *cs->str = '\0'; // 初始分配記憶體大小是32,之後每次以2倍大小擴容 cs->alloced = c_string_min_size; cs->len = 0; return cs; }
銷燬字串:
void c_string_destroy(c_string_t *cs) { if (cs == NULL) return; free(cs->str); free(cs); }
內部如何擴容呢:
static void c_string_ensure_space(c_string_t *cs, size_t add_len) { if (cs == NULL || add_len == 0) return; if (cs->alloced >= cs->len + add_len + 1) return; while (cs->alloced < cs->len + add_len + 1) { cs->alloced <<= 1; // 每次以2倍大小擴容 if (cs->alloced == 0) { // 左移到最後可能會變為0,由於alloced是無符號型,減一則會變成UINT_MAX cs->alloced--; } } cs->str = realloc(cs->str, cs->alloced); }
在尾部追加字串:
void c_string_append_str(c_string_t *cs, const char *str, size_t len) { if (cs == NULL || str == NULL || *str == '\0') return; if (len == 0) len = strlen(str); c_string_ensure_space(cs, len); // 確保內部有足夠的空間儲存字串 memmove(cs->str + cs->len, str, len); cs->len += len; cs->str[cs->len] = '\0'; }
在尾部追加字元:
void c_string_append_char(c_string_t *cs, char c) { if (cs == NULL) return; c_string_ensure_space(cs, 1); cs->str[cs->len] = c; cs->len++; cs->str[cs->len] = '\0'; }
在尾部追加整數:
void c_string_append_int(c_string_t *cs, int val) { char str[12]; if (cs == NULL) return; snprintf(str, sizeof(str), "%d", val); // 整數轉為字串 c_string_append_str(cs, str, 0); }
在頭部插入字串:
void c_string_front_str(c_string_t *cs, const char *str, size_t len) { if (cs == NULL || str == NULL || *str == '\0') return; if (len == 0) len = strlen(str); c_string_ensure_space(cs, len); memmove(cs->str + len, cs->str, cs->len); memmove(cs->str, str, len); cs->len += len; cs->str[cs->len] = '\0'; }
在頭部插入字元:
void c_string_front_char(c_string_t *cs, char c) { if (cs == NULL) return; c_string_ensure_space(cs, 1); memmove(cs->str + 1, cs->str, cs->len); cs->str[0] = c; cs->len++; cs->str[cs->len] = '\0'; }
在頭部插入整數:
void c_string_front_int(c_string_t *cs, int val) { char str[12]; if (cs == NULL) return; snprintf(str, sizeof(str), "%d", val); c_string_front_str(cs, str, 0); }
清空字串:
void c_string_clear(c_string_t *cs) { if (cs == NULL) return; c_string_truncate(cs, 0); }
裁剪字串:
void c_string_truncate(c_string_t *cs, size_t len) { if (cs == NULL || len >= cs->len) return; cs->lenlen = len; cs->str[cs->len] = '\0'; }
刪除頭部的N個字元:
void c_string_drop_begin(c_string_t *cs, size_t len) { if (cs == NULL || len == 0) return; if (len >= cs->len) { c_string_clear(cs); return; } cs->len -= len; memmove(cs->str, cs->str + len, cs->len + 1); }
刪除尾部的N個字元:
void c_string_drop_end(c_string_t *cs, size_t len) { if (cs == NULL || len == 0) return; if (len >= cs->len) { c_string_clear(cs); return; } cs->len -= len; cs->str[cs->len] = '\0'; }
獲取字串的長度:
size_t c_string_len(const c_string_t *cs) { if (cs == NULL) return 0; return cs->len; }
返回字串指標,使用的是內部的記憶體:
const char *c_string_peek(const c_string_t *cs) { if (cs == NULL) return NULL; return cs->str; }
重新分配一塊記憶體儲存字串返回:
char *c_string_dump(const c_string_t *cs, size_t *len) { char *out; if (cs == NULL) return NULL; if (len != NULL) *len = cs->len; out = malloc(cs->len + 1); memcpy(out, cs->str, cs->len + 1); return out; }
測試程式碼如下:
int main() { c_string_t *cs = c_string_create(); c_string_append_str(cs, "123", 0); c_string_append_char(cs, '4'); c_string_append_int(cs, 5); printf("%s \n", c_string_peek(cs)); c_string_front_str(cs, "789", 0); printf("%s \n", c_string_peek(cs)); c_string_drop_begin(cs, 2); printf("%s \n", c_string_peek(cs)); c_string_drop_end(cs, 2); printf("%s \n", c_string_peek(cs)); c_string_destroy(cs); return 0; } 輸出: 12345 78912345 912345 9123 完整程式碼如下:標頭檔案: #includestruct c_string; typedef struct c_string c_string_t; c_string_t *c_string_create(void); void c_string_destroy(c_string_t *cs); void c_string_append_str(c_string_t *cs, const char *str, size_t len); void c_string_append_char(c_string_t *cs, char c); void c_string_append_int(c_string_t *cs, int val); void c_string_front_str(c_string_t *cs, const char *str, size_t len); void c_string_front_char(c_string_t *cs, char c); void c_string_front_int(c_string_t *cs, int val); void c_string_clear(c_string_t *cs); void c_string_truncate(c_string_t *cs, size_t len); void c_string_drop_begin(c_string_t *cs, size_t len); void c_string_drop_end(c_string_t *cs, size_t len); size_t c_string_len(const c_string_t *cs); const char *c_string_peek(const c_string_t *cs); char *c_string_dump(const c_string_t *cs, size_t *len);
原始檔:
#include#include#include#include#includestatic const size_t c_string_min_size = 32; struct c_string { char *str; size_t alloced; size_t len; }; c_string_t *c_string_create(void) { c_string_t *cs; cs = calloc(1, sizeof(*cs)); cs->str = malloc(c_string_min_size); *cs->str = '\0'; cs->alloced = c_string_min_size; cs->len = 0; return cs; } void c_string_destroy(c_string_t *cs) { if (cs == NULL) return; free(cs->str); free(cs); } static void c_string_ensure_space(c_string_t *cs, size_t add_len) { if (cs == NULL || add_len == 0) return; if (cs->alloced >= cs->len + add_len + 1) return; while (cs->alloced < cs->len + add_len + 1) { cs->alloced <<= 1; if (cs->alloced == 0) { cs->alloced--; } } cs->str = realloc(cs->str, cs->alloced); } void c_string_append_str(c_string_t *cs, const char *str, size_t len) { if (cs == NULL || str == NULL || *str == '\0') return; if (len == 0) len = strlen(str); c_string_ensure_space(cs, len); memmove(cs->str + cs->len, str, len); cs->len += len; cs->str[cs->len] = '\0'; } void c_string_append_char(c_string_t *cs, char c) { if (cs == NULL) return; c_string_ensure_space(cs, 1); cs->str[cs->len] = c; cs->len++; cs->str[cs->len] = '\0'; } void c_string_append_int(c_string_t *cs, int val) { char str[12]; if (cs == NULL) return; snprintf(str, sizeof(str), "%d", val); c_string_append_str(cs, str, 0); } void c_string_front_str(c_string_t *cs, const char *str, size_t len) { if (cs == NULL || str == NULL || *str == '\0') return; if (len == 0) len = strlen(str); c_string_ensure_space(cs, len); memmove(cs->str + len, cs->str, cs->len); memmove(cs->str, str, len); cs->len += len; cs->str[cs->len] = '\0'; } void c_string_front_char(c_string_t *cs, char c) { if (cs == NULL) return; c_string_ensure_space(cs, 1); memmove(cs->str + 1, cs->str, cs->len); cs->str[0] = c; cs->len++; cs->str[cs->len] = '\0'; } void c_string_front_int(c_string_t *cs, int val) { char str[12]; if (cs == NULL) return; snprintf(str, sizeof(str), "%d", val); c_string_front_str(cs, str, 0); } void c_string_clear(c_string_t *cs) { if (cs == NULL) return; c_string_truncate(cs, 0); } void c_string_truncate(c_string_t *cs, size_t len) { if (cs == NULL || len >= cs->len) return; cs->lenlen = len; cs->str[cs->len] = '\0'; } void c_string_drop_begin(c_string_t *cs, size_t len) { if (cs == NULL || len == 0) return; if (len >= cs->len) { c_string_clear(cs); return; } cs->len -= len; /* +1 to move the NULL. */ memmove(cs->str, cs->str + len, cs->len + 1); } void c_string_drop_end(c_string_t *cs, size_t len) { if (cs == NULL || len == 0) return; if (len >= cs->len) { c_string_clear(cs); return; } cs->len -= len; cs->str[cs->len] = '\0'; } size_t c_string_len(const c_string_t *cs) { if (cs == NULL) return 0; return cs->len; } const char *c_string_peek(const c_string_t *cs) { if (cs == NULL) return NULL; return cs->str; } char *c_string_dump(const c_string_t *cs, size_t *len) { char *out; if (cs == NULL) return NULL; if (len != NULL) *len = cs->len; out = malloc(cs->len + 1); memcpy(out, cs->str, cs->len + 1); return out; }
原文來自:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2845267/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 基於 golang 實現的泛型陣列,支援動態擴容等特性Golang泛型陣列
- 實現動態自動匹配輸入的內容
- Airbnb的動態 Kubernetes 叢集擴縮容AI
- 非 LVM 分割槽動態擴容LVM
- centos8-LVM捲動態擴容CentOSLVM
- 被vector動態擴容給坑了!
- 如何實現CentOS伺服器的擴容??CentOS伺服器
- C/C++ 實現動態資原始檔釋放C++
- c# ExpandoObject動態擴充套件物件C#Object套件物件
- 有狀態軟體如何在 k8s 上快速擴容甚至自動擴容K8S
- C#實現JWT無狀態驗證的實戰應用C#JWT
- string類的實現
- c++ 從vector擴容看noexcept應用場景C++
- 動態棧的實現
- Golang Map實現(四) map 的賦值和擴容Golang賦值
- LVM邏輯卷線上動態擴容磁碟空間LVM
- 單體系統如何實現動態演進擴充套件套件
- Python擴充套件C/C++ 實現原理分析Python套件C++
- C#動態建立介面的實現例項物件C#物件
- Logback中使用TurboFilter實現日誌級別等內容的動態修改Filter
- c盤擴容怎麼操作 電腦c盤滿了的擴大辦法
- PAT1040 Longest Symmetric String (25分) 中心擴充套件法+動態規劃套件動態規劃
- C盤擴容(步驟記錄)
- kubernetes實踐之四十一:Pod自動擴容與縮容
- 狀態模式(c++實現)模式C++
- 阿里如何將“高峰前擴容、高峰後縮容”的夢想照進現實?阿里
- 阿里如何將「高峰前擴容、高峰後縮容」的夢想照進現實?阿里
- redis string 簡單動態字串Redis字串
- 華納雲:伺服器C盤不夠用如何擴容伺服器
- 又抓了一個導致頻繁GC的鬼--陣列動態擴容GC陣列
- C++容器巢狀實現動態二維陣列C++巢狀陣列
- 動態代理的實際應用
- GO 中 string 的實現原理Go
- Laravel 動態屬性的實現Laravel
- 用js實現動態改變根元素字型大小的方法JS
- Kubernetes:應用自動擴容、收縮與穩定更新
- win10系統c盤擴容後的壞處是什麼 win10c盤擴容怎麼操作Win10
- c++ web框架實現之靜態反射實現C++Web框架反射