社区编辑申请
注册/登录
C语言如何实现动态扩容的string
开发 后端
众所周知,C++ 中的string使用比较方便,关于C++ 中的string源码实现可以看我的这篇文章:源码分析C++的string的实现

众所周知,C++ 中的string使用比较方便,关于C++ 中的string源码实现可以看我的这篇文章:源码分析C++的string的实现

最近工作中使用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. struct c_string {  
  4.     char *str; // 字符串指针  
  5.     size_t alloced; // 已分配的内存大小  
  6.     size_t len; // 字符串的实际长度  
  7. }; 

创建字符串: 

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

销毁字符串: 

  1. void c_string_destroy(c_string_t *cs) {  
  2.     if (cs == NULL) return;  
  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.     if (cs->alloced >= cs->len + add_len + 1) return;  
  4.     while (cs->alloced < cs->len + add_len + 1) {  
  5.         cs->alloced <<= 1; // 每次以2倍大小扩容  
  6.         if (cs->alloced == 0) {  
  7.             // 左移到最后可能会变为0,由于alloced是无符号型,减一则会变成UINT_MAX  
  8.             cs->alloced--;  
  9.         }  
  10.     }  
  11.     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.     if (len == 0) len = strlen(str);  
  4.     c_string_ensure_space(cs, len); // 确保内部有足够的空间存储字符串  
  5.     memmove(cs->str + cs->len, str, len);  
  6.     cs->len += len;  
  7.     cs->str[cs->len] = '\0';  

在尾部追加字符: 

  1. void c_string_append_char(c_string_t *cs, char c) {  
  2.     if (cs == NULL) return;  
  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.     if (cs == NULL) return;  
  4.     snprintf(str, sizeof(str), "%d", val); // 整数转为字符串  
  5.     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.     if (len == 0) len = strlen(str);  
  4.     c_string_ensure_space(cs, len);  
  5.     memmove(cs->str + len, cs->str, cs->len);  
  6.     memmove(cs->str, str, len); 
  7.     cs->len += len;  
  8.     cs->str[cs->len] = '\0';  

在头部插入字符: 

  1. void c_string_front_char(c_string_t *cs, char c) {  
  2.     if (cs == NULL) return;  
  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.     if (cs == NULL) return;  
  4.     snprintf(str, sizeof(str), "%d", val);  
  5.     c_string_front_str(cs, str, 0);  

清空字符串: 

  1. void c_string_clear(c_string_t *cs) {  
  2.     if (cs == NULL) return;  
  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.     cs->lenlen = len;  
  4.     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.     if (len >= cs->len) {  
  4.         c_string_clear(cs);  
  5.         return;  
  6.     }  
  7.     cs->len -len 
  8.     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.     if (len >= cs->len) {  
  4.         c_string_clear(cs);  
  5.         return;  
  6.     }  
  7.     cs->len -len 
  8.     cs->str[cs->len] = '\0';  

获取字符串的长度: 

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

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

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

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

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

测试代码如下: 

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

 

责任编辑:庞桂玉 来源: C语言与C++编程
相关推荐

2020-10-23 06:56:00

C语言动态字符串

2022-04-12 11:38:06

C语言全局变量

2022-01-13 10:30:21

2022-04-20 20:28:40

HDF 驱动框架鸿蒙操作系统

2022-03-29 08:30:47

2022-05-03 23:44:21

Python动态链接库Ctypes

2022-05-10 16:04:40

编程语言PythonC语言

2021-12-30 11:26:31

2022-03-16 10:14:55

C语言C++

2022-04-01 15:18:04

HarmonyHDF 驱动鸿蒙

2022-04-22 18:48:46

LinuxLinux 内核C 语言

2022-02-25 14:13:28

LinuxC语言开发

2022-04-21 09:26:41

FastDFS开源分布式文件系统

2022-03-08 11:17:54

函数指针回调函数C语言

2022-04-12 11:20:11

C 语言Linux编程

2022-04-06 13:55:22

DockerLinux

2021-03-15 14:00:56

2020-03-30 09:22:36

C语言结构体

2022-04-13 09:57:24

Go语言C语言程序开发

2019-09-29 10:45:46

C语言CPU编译器

同话题下的热门内容

Python 字符串总结,建议收藏!这份Java日志格式规范,拿走不谢!Mybatis-Plus官方发布分库分表神器,一个依赖轻松搞定!后端思维篇:如何应用设计模式优化代码改变 Python 对象规则的黑魔法 Metaclass几种限流算法的Go语言实现JMeter关联之正则表达式提取器在 Go 中实现一个支持并发的 TCP 服务端

编辑推荐

使用Kotlin做开发一个月后的感想面试官问你什么是消息队列?把这篇甩给他!五大自动化测试的Python框架图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS)2018年最流行的十大编程语言,其中包括你用的语言吗?
我收藏的内容
点赞
收藏

51CTO技术栈公众号