C/C++知识点之C语言如何实现动态扩容的String
小职 2020-10-23 来源 :程序喵大人 阅读 740 评论 0

摘要:本篇介绍了C语言如何高效的实现一个不需要处理内存问题并且可以动态扩容进行拼接和裁剪的string,希望对C/C++的学习有所帮助。

本篇介绍了C语言如何高效的实现一个不需要处理内存问题并且可以动态扩容进行拼接和裁剪的string,希望对C/C++的学习有所帮助。

C/C++知识点之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->len = 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

完整代码如下:头文件:

 

#include <stddef.h>

 

struct 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 <ctype.h>

#include <stdbool.h>

#include <stdlib.h>

#include <stdio.h>

#include <string.h>

 

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

    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->len = 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;




关注“职坐标在线”公众号,免费获取最新技术干货教程资源哦!

本文由 @小职 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程