百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

C语言实现动态扩容的string(c语言动态内存分配malloc)

liebian365 2025-03-28 18:17 17 浏览 0 评论

众所周知,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;
  10. }

销毁字符串:

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

内部如何扩容呢:

  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);
  12. }

在尾部追加字符串:

  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';
  8. }

在尾部追加字符:

  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';
  7. }

在尾部追加整数:

  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);
  6. }

在头部插入字符串:

  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';
  9. }

在头部插入字符:

  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';
  8. }

在头部插入整数:

  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);
  6. }

清空字符串:

  1. void c_string_clear(c_string_t *cs) {
  2. if (cs == NULL) return;
  3. c_string_truncate(cs, 0);
  4. }

裁剪字符串:

  1. void c_string_truncate(c_string_t *cs, size_t len) {
  2. if (cs == NULL || len >= cs->len) return;

  3. cs->len = len;
  4. cs->str[cs->len] = '\0';
  5. }

删除头部的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);
  9. }

删除尾部的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';
  9. }

获取字符串的长度:

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

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

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

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

  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;
  15. }

输出:

12345
78912345
912345
9123

完整代码如下:头文件:

  1. #include

  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
  2. #include
  3. #include
  4. #include
  5. #include

  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->len = 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. }

相关推荐

“版本末期”了?下周平衡补丁!国服最强5套牌!上分首选

明天,酒馆战棋就将迎来大更新,也聊了很多天战棋相关的内容了,趁此机会,给兄弟们穿插一篇构筑模式的卡组推荐!老规矩,我们先来看10职业胜率。目前10职业胜率排名与一周前基本类似,没有太多的变化。平衡补丁...

VS2017 C++ 程序报错“error C2065:“M_PI”: 未声明的标识符&quot;

首先,程序中头文件的选择,要选择头文件,在文件中是没有对M_PI的定义的。选择:项目——>”XXX属性"——>配置属性——>C/C++——>预处理器——>预处理器定义,...

东营交警实名曝光一批酒驾人员名单 88人受处罚

齐鲁网·闪电新闻5月24日讯酒后驾驶是对自己和他人生命安全极不负责的行为,为守护大家的平安出行路,东营交警一直将酒驾作为重点打击对象。5月23日,东营交警公布最新一批饮酒、醉酒名单。对以下驾驶人醉酒...

Qt界面——搭配QCustomPlot(qt platform)

这是我第一个使用QCustomPlot控件的上位机,通过串口精确的5ms发送一次数据,再将读取的数据绘制到图表中。界面方面,尝试卡片式设计,外加QSS简单的配了个色。QCustomPlot官网:Qt...

大话西游2分享赢取种族坐骑手办!PK趣闻录由你书写

老友相聚,仗剑江湖!《大话西游2》2021全民PK季4月激燃打响,各PK玩法鏖战齐开,零门槛参与热情高涨。PK季期间,不仅各种玩法奖励丰厚,参与PK趣闻录活动,投稿自己在PK季遇到的趣事,还有机会带走...

测试谷歌VS Code AI 编程插件 Gemini Code Assist

用ClaudeSonnet3.7的天气测试编码,让谷歌VSCodeAI编程插件GeminiCodeAssist自动编程。生成的文件在浏览器中的效果如下:(附源代码)VSCode...

顾爷想知道第4.5期 国服便利性到底需优化啥?

前段时间DNF国服推出了名为“阿拉德B计划”的系列改版计划,截至目前我们已经看到了两项实装。不过关于便利性上,国服似乎还有很多路要走。自从顾爷回归DNF以来,几乎每天都在跟我抱怨关于DNF里面各种各样...

掌握Visual Studio项目配置【基础篇】

1.前言VisualStudio是Windows上最常用的C++集成开发环境之一,简称VS。VS功能十分强大,对应的,其配置系统较为复杂。不管是对于初学者还是有一定开发经验的开发者来说,捋清楚VS...

还嫌LED驱动设计套路深?那就来看看这篇文章吧

随着LED在各个领域的不同应用需求,LED驱动电路也在不断进步和发展。本文从LED的特性入手,推导出适合LED的电源驱动类型,再进一步介绍各类LED驱动设计。设计必读:LED四个关键特性特性一:非线...

Visual Studio Community 2022(VS2022)安装图文方法

直接上步骤:1,首先可以下载安装一个VisualStudio安装器,叫做VisualStudioinstaller。这个安装文件很小,很快就安装完成了。2,打开VisualStudioins...

Qt添加MSVC构建套件的方法(qt添加c++11)

前言有些时候,在Windows下因为某些需求需要使用MSVC编译器对程序进行编译,假设我们安装Qt的时候又只是安装了MingW构建套件,那么此时我们该如何给现有的Qt添加一个MSVC构建套件呢?本文以...

Qt为什么站稳c++GUI的top1(qt c)

为什么现在QT越来越成为c++界面编程的第一选择,从事QT编程多年,在这之前做C++界面都是基于MFC。当时为什么会从MFC转到QT?主要原因是MFC开发界面想做得好看一些十分困难,引用第三方基于MF...

qt开发IDE应该选择VS还是qt creator

如果一个公司选择了qt来开发自己的产品,在面临IDE的选择时会出现vs或者qtcreator,选择qt的IDE需要结合产品需求、部署平台、项目定位、程序猿本身和公司战略,因为大的软件产品需要明确IDE...

Qt 5.14.2超详细安装教程,不会来打我

Qt简介Qt(官方发音[kju:t],音同cute)是一个跨平台的C++开库,主要用来开发图形用户界面(GraphicalUserInterface,GUI)程序。Qt是纯C++开...

Cygwin配置与使用(四)——VI字体和颜色的配置

简介:VI的操作模式,基本上VI可以分为三种状态,分别是命令模式(commandmode)、插入模式(Insertmode)和底行模式(lastlinemode),各模式的功能区分如下:1)...

取消回复欢迎 发表评论: