C语言如何实现动态扩容的String

开发 后端
最近工作中使用C语言,但又苦于没有高效的字符串实现,字符串的拼接和裁剪都比较麻烦,而且每个字符串都需要申请内存,内存的申请和释放也很容易出bug,怎么高效的实现一个不需要处理内存问题并且可以动态扩容进行拼接和裁剪的string呢?

[[347888]]

本文转载自微信公众号「程序喵大人」,作者程序喵大人 。转载本文请联系程序喵大人公众号。

最近工作中使用C语言,但又苦于没有高效的字符串实现,字符串的拼接和裁剪都比较麻烦,而且每个字符串都需要申请内存,内存的申请和释放也很容易出bug,怎么高效的实现一个不需要处理内存问题并且可以动态扩容进行拼接和裁剪的string呢?

  • 一个好的string应该有以下功能?
  • 创建字符串
  • 删除字符串
  • 尾部追加字符串
  • 头部插入字符串
  • 从尾部删除N个字符
  • 从头部删除N个字符
  • 裁剪字符串
  • 获取字符串长度
  • 获取完整字符串

下面来看看各个功能的实现:

首先定义一个string的句柄,相当于C++中的实例

  1. struct c_string; 
  2. typedef struct c_string c_string_t; 

在内部string的实现如下:

  1. // string的初始内存大小 
  2. static const size_t c_string_min_size = 32; 
  3.  
  4. struct c_string { 
  5.     char *str; // 字符串指针 
  6.     size_t alloced; // 已分配的内存大小 
  7.     size_t len; // 字符串的实际长度 
  8. }; 

创建字符串:

  1. c_string_t *c_string_create(void) { 
  2.     c_string_t *cs; 
  3.  
  4.     cs = calloc(1, sizeof(*cs)); 
  5.     cs->str = malloc(c_string_min_size); 
  6.     *cs->str = '\0'
  7.     // 初始分配内存大小是32,之后每次以2倍大小扩容 
  8.     cs->alloced = c_string_min_size;  
  9.     cs->len = 0; 
  10.  
  11.     return cs; 

销毁字符串:

  1. void c_string_destroy(c_string_t *cs) { 
  2.     if (cs == NULLreturn
  3.     free(cs->str); 
  4.     free(cs); 

内部如何扩容呢:

  1. static void c_string_ensure_space(c_string_t *cs, size_t add_len) { 
  2.     if (cs == NULL || add_len == 0) return
  3.  
  4.     if (cs->alloced >= cs->len + add_len + 1) return
  5.  
  6.     while (cs->alloced < cs->len + add_len + 1) { 
  7.         cs->alloced <<= 1; // 每次以2倍大小扩容 
  8.         if (cs->alloced == 0) { 
  9.             // 左移到最后可能会变为0,由于alloced是无符号型,减一则会变成UINT_MAX 
  10.             cs->alloced--; 
  11.         } 
  12.     } 
  13.     cs->str = realloc(cs->str, cs->alloced); 

在尾部追加字符串:

  1. void c_string_append_str(c_string_t *cs, const char *str, size_t len) { 
  2.     if (cs == NULL || str == NULL || *str == '\0'return
  3.  
  4.     if (len == 0) len = strlen(str); 
  5.  
  6.     c_string_ensure_space(cs, len); // 确保内部有足够的空间存储字符串 
  7.     memmove(cs->str + cs->len, str, len); 
  8.     cs->len += len; 
  9.     cs->str[cs->len] = '\0'

在尾部追加字符:

  1. void c_string_append_char(c_string_t *cs, char c) { 
  2.     if (cs == NULLreturn
  3.     c_string_ensure_space(cs, 1); 
  4.     cs->str[cs->len] = c; 
  5.     cs->len++; 
  6.     cs->str[cs->len] = '\0'

在尾部追加整数:

  1. void c_string_append_int(c_string_t *cs, int val) { 
  2.     char str[12]; 
  3.  
  4.     if (cs == NULLreturn
  5.  
  6.     snprintf(str, sizeof(str), "%d", val); // 整数转为字符串 
  7.     c_string_append_str(cs, str, 0); 

在头部插入字符串:

  1. void c_string_front_str(c_string_t *cs, const char *str, size_t len) { 
  2.     if (cs == NULL || str == NULL || *str == '\0'return
  3.  
  4.     if (len == 0) len = strlen(str); 
  5.  
  6.     c_string_ensure_space(cs, len); 
  7.     memmove(cs->str + len, cs->str, cs->len); 
  8.     memmove(cs->str, str, len); 
  9.     cs->len += len; 
  10.     cs->str[cs->len] = '\0'

在头部插入字符:

  1. void c_string_front_char(c_string_t *cs, char c) { 
  2.     if (cs == NULLreturn
  3.     c_string_ensure_space(cs, 1); 
  4.     memmove(cs->str + 1, cs->str, cs->len); 
  5.     cs->str[0] = c; 
  6.     cs->len++; 
  7.     cs->str[cs->len] = '\0'

在头部插入整数:

  1. void c_string_front_int(c_string_t *cs, int val) { 
  2.     char str[12]; 
  3.  
  4.     if (cs == NULLreturn
  5.  
  6.     snprintf(str, sizeof(str), "%d", val); 
  7.     c_string_front_str(cs, str, 0); 

清空字符串:

  1. void c_string_clear(c_string_t *cs) { 
  2.     if (cs == NULLreturn
  3.     c_string_truncate(cs, 0); 

裁剪字符串:

  1. void c_string_truncate(c_string_t *cs, size_t len) { 
  2.     if (cs == NULL || len >= cs->len) return
  3.  
  4.     cs->len = len; 
  5.     cs->str[cs->len] = '\0'

删除头部的N个字符:

  1. void c_string_drop_begin(c_string_t *cs, size_t len) { 
  2.     if (cs == NULL || len == 0) return
  3.  
  4.     if (len >= cs->len) { 
  5.         c_string_clear(cs); 
  6.         return
  7.     } 
  8.  
  9.     cs->len -= len; 
  10.     memmove(cs->str, cs->str + len, cs->len + 1); 

删除尾部的N个字符:

  1. void c_string_drop_end(c_string_t *cs, size_t len) { 
  2.     if (cs == NULL || len == 0) return
  3.  
  4.     if (len >= cs->len) { 
  5.         c_string_clear(cs); 
  6.         return
  7.     } 
  8.     cs->len -= len; 
  9.     cs->str[cs->len] = '\0'

获取字符串的长度:

  1. size_t c_string_len(const c_string_t *cs) { 
  2.     if (cs == NULLreturn 0; 
  3.     return cs->len; 

返回字符串指针,使用的是内部的内存:

  1. const char *c_string_peek(const c_string_t *cs) { 
  2.     if (cs == NULLreturn NULL
  3.     return cs->str; 

重新分配一块内存存储字符串返回:

  1. char *c_string_dump(const c_string_t *cs, size_t *len) { 
  2.     char *out
  3.  
  4.     if (cs == NULLreturn NULL
  5.  
  6.     if (len != NULL) *len = cs->len; 
  7.     out = malloc(cs->len + 1); 
  8.     memcpy(out, cs->str, cs->len + 1); 
  9.     return out

测试代码如下:

  1. int main() { 
  2.     c_string_t *cs = c_string_create(); 
  3.     c_string_append_str(cs, "123", 0); 
  4.     c_string_append_char(cs, '4'); 
  5.     c_string_append_int(cs, 5); 
  6.     printf("%s \n", c_string_peek(cs)); 
  7.     c_string_front_str(cs, "789", 0); 
  8.     printf("%s \n", c_string_peek(cs)); 
  9.     c_string_drop_begin(cs, 2); 
  10.     printf("%s \n", c_string_peek(cs)); 
  11.     c_string_drop_end(cs, 2); 
  12.     printf("%s \n", c_string_peek(cs)); 
  13.     c_string_destroy(cs); 
  14.     return 0; 

输出:

  1. 12345 
  2. 78912345 
  3. 912345 
  4. 9123 

完整代码如下:头文件:

  1. #include <stddef.h> 
  2.  
  3. struct c_string; 
  4. typedef struct c_string c_string_t; 
  5.  
  6. c_string_t *c_string_create(void); 
  7.  
  8. void c_string_destroy(c_string_t *cs); 
  9.  
  10. void c_string_append_str(c_string_t *cs, const char *str, size_t len); 
  11.  
  12. void c_string_append_char(c_string_t *cs, char c); 
  13.  
  14. void c_string_append_int(c_string_t *cs, int val); 
  15.  
  16. void c_string_front_str(c_string_t *cs, const char *str, size_t len); 
  17.  
  18. void c_string_front_char(c_string_t *cs, char c); 
  19.  
  20. void c_string_front_int(c_string_t *cs, int val); 
  21.  
  22. void c_string_clear(c_string_t *cs); 
  23.  
  24. void c_string_truncate(c_string_t *cs, size_t len); 
  25.  
  26. void c_string_drop_begin(c_string_t *cs, size_t len); 
  27.  
  28. void c_string_drop_end(c_string_t *cs, size_t len); 
  29.  
  30. size_t c_string_len(const c_string_t *cs); 
  31.  
  32. const char *c_string_peek(const c_string_t *cs); 
  33.  
  34. char *c_string_dump(const c_string_t *cs, size_t *len); 

源文件:

  1. #include <ctype.h> 
  2. #include <stdbool.h> 
  3. #include <stdlib.h> 
  4. #include <stdio.h> 
  5. #include <string.h> 
  6.  
  7. static const size_t c_string_min_size = 32; 
  8.  
  9. struct c_string { 
  10.     char *str; 
  11.     size_t alloced; 
  12.     size_t len; 
  13. }; 
  14.  
  15. c_string_t *c_string_create(void) { 
  16.     c_string_t *cs; 
  17.  
  18.     cs = calloc(1, sizeof(*cs)); 
  19.     cs->str = malloc(c_string_min_size); 
  20.     *cs->str = '\0'
  21.     cs->alloced = c_string_min_size; 
  22.     cs->len = 0; 
  23.  
  24.     return cs; 
  25.  
  26. void c_string_destroy(c_string_t *cs) { 
  27.     if (cs == NULLreturn
  28.     free(cs->str); 
  29.     free(cs); 
  30.  
  31. static void c_string_ensure_space(c_string_t *cs, size_t add_len) { 
  32.     if (cs == NULL || add_len == 0) return
  33.  
  34.     if (cs->alloced >= cs->len + add_len + 1) return
  35.  
  36.     while (cs->alloced < cs->len + add_len + 1) { 
  37.         cs->alloced <<= 1; 
  38.         if (cs->alloced == 0) { 
  39.             cs->alloced--; 
  40.         } 
  41.     } 
  42.     cs->str = realloc(cs->str, cs->alloced); 
  43.  
  44. void c_string_append_str(c_string_t *cs, const char *str, size_t len) { 
  45.     if (cs == NULL || str == NULL || *str == '\0'return
  46.  
  47.     if (len == 0) len = strlen(str); 
  48.  
  49.     c_string_ensure_space(cs, len); 
  50.     memmove(cs->str + cs->len, str, len); 
  51.     cs->len += len; 
  52.     cs->str[cs->len] = '\0'
  53.  
  54. void c_string_append_char(c_string_t *cs, char c) { 
  55.     if (cs == NULLreturn
  56.     c_string_ensure_space(cs, 1); 
  57.     cs->str[cs->len] = c; 
  58.     cs->len++; 
  59.     cs->str[cs->len] = '\0'
  60.  
  61. void c_string_append_int(c_string_t *cs, int val) { 
  62.     char str[12]; 
  63.  
  64.     if (cs == NULLreturn
  65.  
  66.     snprintf(str, sizeof(str), "%d", val); 
  67.     c_string_append_str(cs, str, 0); 
  68.  
  69. void c_string_front_str(c_string_t *cs, const char *str, size_t len) { 
  70.     if (cs == NULL || str == NULL || *str == '\0'return
  71.  
  72.     if (len == 0) len = strlen(str); 
  73.  
  74.     c_string_ensure_space(cs, len); 
  75.     memmove(cs->str + len, cs->str, cs->len); 
  76.     memmove(cs->str, str, len); 
  77.     cs->len += len; 
  78.     cs->str[cs->len] = '\0'
  79.  
  80. void c_string_front_char(c_string_t *cs, char c) { 
  81.     if (cs == NULLreturn
  82.     c_string_ensure_space(cs, 1); 
  83.     memmove(cs->str + 1, cs->str, cs->len); 
  84.     cs->str[0] = c; 
  85.     cs->len++; 
  86.     cs->str[cs->len] = '\0'
  87.  
  88. void c_string_front_int(c_string_t *cs, int val) { 
  89.     char str[12]; 
  90.  
  91.     if (cs == NULLreturn
  92.  
  93.     snprintf(str, sizeof(str), "%d", val); 
  94.     c_string_front_str(cs, str, 0); 
  95.  
  96. void c_string_clear(c_string_t *cs) { 
  97.     if (cs == NULLreturn
  98.     c_string_truncate(cs, 0); 
  99.  
  100. void c_string_truncate(c_string_t *cs, size_t len) { 
  101.     if (cs == NULL || len >= cs->len) return
  102.  
  103.     cs->len = len; 
  104.     cs->str[cs->len] = '\0'
  105.  
  106. void c_string_drop_begin(c_string_t *cs, size_t len) { 
  107.     if (cs == NULL || len == 0) return
  108.  
  109.     if (len >= cs->len) { 
  110.         c_string_clear(cs); 
  111.         return
  112.     } 
  113.  
  114.     cs->len -= len; 
  115.     /* +1 to move the NULL. */ 
  116.     memmove(cs->str, cs->str + len, cs->len + 1); 
  117.  
  118. void c_string_drop_end(c_string_t *cs, size_t len) { 
  119.     if (cs == NULL || len == 0) return
  120.  
  121.     if (len >= cs->len) { 
  122.         c_string_clear(cs); 
  123.         return
  124.     } 
  125.     cs->len -= len; 
  126.     cs->str[cs->len] = '\0'
  127.  
  128. size_t c_string_len(const c_string_t *cs) { 
  129.     if (cs == NULLreturn 0; 
  130.     return cs->len; 
  131.  
  132. const char *c_string_peek(const c_string_t *cs) { 
  133.     if (cs == NULLreturn NULL
  134.     return cs->str; 
  135.  
  136. char *c_string_dump(const c_string_t *cs, size_t *len) { 
  137.     char *out
  138.  
  139.     if (cs == NULLreturn NULL
  140.  
  141.     if (len != NULL) *len = cs->len; 
  142.     out = malloc(cs->len + 1); 
  143.     memcpy(out, cs->str, cs->len + 1); 
  144.     return out

 

责任编辑:武晓燕 来源: 程序喵大人
相关推荐

2021-11-19 11:36:42

语言string字符串

2023-04-03 08:02:16

切片扩容GO

2022-10-21 09:01:41

StudentC++类型

2010-03-11 19:16:32

Python语言

2020-08-26 14:00:37

C++string语言

2022-01-13 10:30:21

C语言内存动态

2023-10-26 11:03:50

C语言宏定义

2011-07-05 17:07:14

C语言

2010-07-07 11:09:36

UDPClient

2021-05-10 20:58:11

数据库扩容用户

2010-01-14 17:58:41

C++语言

2014-08-05 13:09:34

Objective-C动态特性

2010-01-04 19:14:46

Silverlight

2024-08-13 09:16:30

2010-01-28 15:31:34

学习C++语言

2010-12-01 15:28:19

ProbeVueJava

2020-06-19 12:59:33

动态脚本Java

2009-08-28 16:14:26

C#实现加载动态库

2022-09-02 17:07:46

C语言main()

2021-06-26 07:29:42

RedisHashtable数据
点赞
收藏

51CTO技术栈公众号