动态字符串(C语言)

动态字符串

自己实现了一个可以动态增长的字符串,可以当作普通字符串使用。

用法示例:

// 编译:gcc dstring.c main.c -o main

#include <stdio.h>    // printf()
#include "dstring.h"

// 用于辅助测试的函数(查看动态字符串的内容、长度、容量)
void show_ds(char *s)
{
	printf("--- %s\n", s);
	printf("%lu\n", ds_len(s));
	printf("%lu\n", ds_cap(s));
}

// 执行测试
int main(void)
{
	char *s;
	char **ps = &s;

	// 创建动态字符串
	s = new_ds(0);

	// 查看字符串变量地址
	printf("s = %p\n", s);
	// s = 0x56459c7f62c0

	// 查看字符串内容、长度、容量
	show_ds(s);
	// ---
	// 0
	// 0

	// 添加字符
	ds_add_char(ps, 'A');
	show_ds(s);
	// --- A
	// 1
	// 16

	// 添加字符串
	ds_add_str(ps, "  Hello World!");
	show_ds(s);
	// --- A  Hello World!
	// 15
	// 16

	// 添加多字符串
	ds_add_strs(ps, 4, "  Hello", " Dynamic", " String", "!");
	show_ds(s);
	// --- A  Hello World!  Hello Dynamic String!
	// 38
	// 44

	// 申请空间
	ds_grow(ps, 8*1024*1024);
	show_ds(s);
	// --- A  Hello World!  Hello Dynamic String!
	// 38
	// 16791488

	// 压缩空间
	ds_pack(ps);
	show_ds(s);
	// --- A  Hello World!  Hello Dynamic String!
	// 38
	// 38

	// 调整字符串大小
	ds_resize(ps, 12);
	show_ds(s);
	// --- A  Hello Wor
	// 12
	// 12

	// 插入完整字符串
	ds_ins_str(ps, 3, "+++++  ", 0);
	show_ds(s);
	// --- A  +++++  Hello Wor
	// 19
	// 38

	// 插入部分字符串
	ds_ins_str(ps, 1, "+++++  ", 2);
	show_ds(s);
	// --- A++  +++++  Hello Wor
	// 21
	// 38

	// 获取最后一个字符
	printf("last_ch = %c\n", ds_last(s));
	// last_ch = r

	// 获取最后一个字符之后的位置
	printf("end_pos = %lu\n", ds_end(s) - s);
	// end_pos = 21

	// 释放动态字符串
	free_ds(ps);

	// 查看字符串变量地址
	printf("s = %p\n", s);
	// s = (nil)
}

头文件 dstring.h

#ifndef DSTRING_H__
#define DSTRING_H__

#include <stddef.h>  // size_t
#include <stdint.h>  // uint64_t


/* ------------------------------------------------------------
   new dynamic string
   功能:创建动态字符串
   参数:要预留的缓冲区大小
   返回:动态字符串变量(即:缓冲区中数据的地址)
------------------------------------------------------------ */
char * new_ds(size_t size);


/* ------------------------------------------------------------
   free dynamic string
   功能:释放动态字符串,并将 *ps 设置为 NULL
   参数:动态字符串变量的地址
   返回:无
------------------------------------------------------------ */
void free_ds(char **ps);


/* ------------------------------------------------------------
   is dynamic string
   功能:判断字符串是否为动态字符串
   参数:字符串变量
   返回:是返回 1,不是返回 0
------------------------------------------------------------ */
int is_ds(const char *s);


/* ------------------------------------------------------------
   dynamic string custom data
   功能:获取动态字符串的自定义数据
   参数:动态字符串变量
   返回:自定义数据
------------------------------------------------------------ */
uint64_t ds_custom(const char *s);


/* ------------------------------------------------------------
   dynamic string custom data
   功能:设置动态字符串的自定义数据
   参数:动态字符串变量,定义数据
   返回:无
------------------------------------------------------------ */
void ds_set_custom(char *s, uint64_t custom);


/* ------------------------------------------------------------
   dynamic string length
   功能:获取动态字符串的长度
   参数:动态字符串变量
   返回:字符串长度(不包括尾部的 '\0')
------------------------------------------------------------ */
uint64_t ds_len(const char *s);


/* ------------------------------------------------------------
   dynamic string capacity
   功能:获取动态字符串的缓冲区容量
   参数:动态字符串变量
   返回:缓冲区容量(不包括尾部的 '\0')
------------------------------------------------------------ */
uint64_t ds_cap(const char *s);


/* ------------------------------------------------------------
   dynamic string last char
   功能:获取动态字符串的最后一个字符
   参数:动态字符串变量
   返回:最后一个字符,如果字符串为空,则返回 '\0'
------------------------------------------------------------ */
int ds_last(char *s);


/* ------------------------------------------------------------
   dynamic string end position
   功能:获取动态字符串最后一个字符之后的位置
   参数:动态字符串变量
   返回:位置指针
------------------------------------------------------------ */
char * ds_end(char *s);


/* ------------------------------------------------------------
   grow dynamic string
   功能:根据 needsize 判断是否需要扩容,根据需要执行扩容
   参数:动态字符串变量的地址,要申请的可用空间大小
   返回:动态字符串变量
------------------------------------------------------------ */
char * ds_grow(char **ps, size_t needsize);


/* ------------------------------------------------------------
   resize dynamic string capacity
   功能:调整动态字符串的容量(会自动在容量后面添加 '\0')
   参数:动态字符串变量的地址,新的容量
   返回:动态字符串变量
------------------------------------------------------------ */
char * ds_resize(char **ps, size_t cap);


/* ------------------------------------------------------------
   pack dynamic string
   功能:压缩动态字符串(释放预留空间,只保留数据部分)
   参数:动态字符串变量的地址
   返回:动态字符串变量
------------------------------------------------------------ */
char * ds_pack(char **ps);


/* ------------------------------------------------------------
   dynamic string add char
   功能:向动态字符串中追加字符
   参数:动态字符串变量的地址,要写入的字符
   返回:动态字符串变量
------------------------------------------------------------ */
char * ds_add_char(char **ps, char c);


/* ------------------------------------------------------------
   dynamic string add string
   功能:向动态字符串中追加字符串
   参数:动态字符串变量的地址,要写入的字符串
   返回:动态字符串变量
------------------------------------------------------------ */
char * ds_add_str(char **ps, const char* sub);


/* ------------------------------------------------------------
   dynamic string add strings
   功能:向动态字符串中追加多个字符串
   参数:动态字符串变量的地址,要写入的字符串个数,字符串列表
   返回:动态字符串变量
------------------------------------------------------------ */
char * ds_add_strs(char **ps, int count, ...);


/* ------------------------------------------------------------
   dynamic string insert numbered string
   功能:在动态字符串的指定位置插入指定长度的子串
   参数:插入点、要插入的子串、子串长度(0 表示全部)
   返回:动态字符串变量
------------------------------------------------------------ */
char * ds_ins_str(char **ps, size_t pos, const char *sub, size_t len);


#endif

源文件 dstring.c

#include <stdio.h>   // perror()
#include <stdlib.h>  // malloc()  realloc()  exit()
#include <stdarg.h>  // va_start()  va_arg()  va_end()
#include <string.h>  // memcpy()  strlen()

#include "dstring.h"

/* ============================================================
功能:实现动态字符串(缓冲区容量根据需要自动扩展)
	  动态字符串可以当作普通字符串使用(和普通串特征相同)

内存布局(内存占用 = 32 + 缓冲区长度 + 1):

 0 -  7:动态字符串标记 "\xDA\xDA""DSTR""\xDC\xDC"(魔法数字)
 8 - 15:自定义,可用于存放编码类型等
24 - 31:字符串长度(字节)
16 - 23:缓冲区长度(字节)
32 -  n:缓冲区空间
n + 1  :'\0' 字符,保证缓冲区以 '\0' 结尾
============================================================ */

// 动态字符串结构体
typedef struct {
	uint64_t magic;   // 魔法数字
	uint64_t custom;  // 自定义数据
	uint64_t len;     // 字符串长度(不包括最后的 \0)
	uint64_t cap;     // 缓冲区长度(不包括最后的 \0)
	char data[];      // 缓冲区(始终以 \0 结尾)
} dstr;

static const int DS_HEADER_SIZE = sizeof(uint64_t) * 4;   // 头部大小
static const uint64_t MAGIC_NUMBER = 0xDCDC52545344DADA;  // 魔法数字

// 从数据区(data)到各个字段的 uint64_t 偏移量
static const int OFF_MAGIC	 = -4;
static const int OFF_CUSTOM	 = -3;
static const int OFF_LEN     = -2;
static const int OFF_CAP     = -1;


/* ------------------------------------------------------------
   dynamic string get header
   功能:从动态字符串的数据位置跳转到头部位置
   参数:动态字符串变量
   返回:动态字符串结构体指针
------------------------------------------------------------ */
static dstr * ds_header(char *s)
{
	dstr *ds = (dstr *)(s - DS_HEADER_SIZE);

	if (ds->magic != MAGIC_NUMBER)
		return NULL;
	else
		return ds;
}


/* ------------------------------------------------------------
   new dynamic string
   功能:创建动态字符串
   参数:要预留的缓冲区大小
   返回:动态字符串变量(即:缓冲区中数据的地址)
------------------------------------------------------------ */
char * new_ds(size_t cap)
{
	dstr *ds;

	// 预留尾部的 '\0' 字节
	ds = malloc(DS_HEADER_SIZE + cap + 1);
	if (ds == NULL)
	{
		perror("newds()->malloc()");
		exit(1);
	}

	ds->magic   = MAGIC_NUMBER;
	ds->custom  = 0;
	ds->cap     = cap;
	ds->len     = 0;
	// 因为申请内存时 +1 个字节,所以当参数 cap 为 0 时才能写入这个字节
	ds->data[0] = '\0';

	return ds->data;
}


/* ------------------------------------------------------------
   free dynamic string
   功能:释放动态字符串,并将 *ps 设置为 NULL
   参数:动态字符串变量的地址
   返回:无
------------------------------------------------------------ */
void free_ds(char **ps)
{
	dstr * ds = ds_header(*ps);

	if (ds == NULL)
	{
		printf("freeds(): not a dynamic string");
		exit(1);
	}

	free(ds);
	*ps = NULL;
}


/* ------------------------------------------------------------
   is dynamic string
   功能:判断字符串是否为动态字符串
   参数:字符串变量
   返回:是返回 1,不是返回 0
------------------------------------------------------------ */
int is_ds(const char *s)
{
	return ((uint64_t *)s)[OFF_MAGIC] == MAGIC_NUMBER;
}


/* ------------------------------------------------------------
   dynamic string custom data
   功能:获取动态字符串的自定义数据
   参数:动态字符串变量
   返回:自定义数据
------------------------------------------------------------ */
uint64_t ds_custom(const char *s)
{
	return ((uint64_t *)s)[OFF_CUSTOM];
}


/* ------------------------------------------------------------
   dynamic string custom data
   功能:设置动态字符串的自定义数据
   参数:动态字符串变量,定义数据
   返回:无
------------------------------------------------------------ */
void ds_set_custom(char *s, uint64_t custom)
{
	((uint64_t *)s)[OFF_CUSTOM] = custom;
}


/* ------------------------------------------------------------
   dynamic string length
   功能:获取动态字符串的长度
   参数:动态字符串变量
   返回:字符串长度(不包括尾部的 '\0')
------------------------------------------------------------ */
uint64_t ds_len(const char *s)
{
	return ((uint64_t *)s)[OFF_LEN];
}

static inline void ds_set_len(char *s, uint64_t len)
{
	((uint64_t *)s)[OFF_LEN] = len;
	s[len] = '\0';
}


/* ------------------------------------------------------------
   dynamic string capacity
   功能:获取动态字符串的缓冲区容量
   参数:动态字符串变量
   返回:缓冲区容量(不包括尾部的 '\0')
------------------------------------------------------------ */
uint64_t ds_cap(const char *s)
{
	return ((uint64_t *)s)[OFF_CAP];
}


static inline void ds_set_cap(char *s, uint64_t cap)
{
	((uint64_t *)s)[OFF_CAP] = cap;
	s[cap] = '\0';
}


/* ------------------------------------------------------------
   dynamic string last char
   功能:获取动态字符串的最后一个字符
   参数:动态字符串变量
   返回:最后一个字符,如果字符串为空,则返回 '\0'
------------------------------------------------------------ */
int ds_last(char *s)
{
	uint64_t len = ds_len(s);

	if (len == 0)
		return '\0';
	else
		return s[len - 1];
}


/* ------------------------------------------------------------
   dynamic string end position
   功能:获取动态字符串最后一个字符之后的位置
   参数:动态字符串变量
   返回:位置指针
------------------------------------------------------------ */
char * ds_end(char *s)
{
	return s + ds_len(s);
}


/* ------------------------------------------------------------
   grow dynamic string
   功能:根据 needsize 判断是否需要扩容,根据需要执行扩容
   参数:动态字符串变量的地址,要申请的可用空间大小
   返回:动态字符串变量
------------------------------------------------------------ */
// 使用 Fast Inverse Square Root 算法快速求平方根
float fast_sqrt(float x)
{
	float xhalf = 0.5f * x;
	int i = *(int*)&x;               // 获取浮点数的二进制位
	i = 0x5f375a86 - (i >> 1);       // 给出初始猜测 y0
	x = *(float*)&i;                 // 将二进制位转换回浮点
	x = x * (1.5f - xhalf * x * x);  // 牛顿步骤,重复提高精度
	return 1/x;
}

char * ds_grow(char **ps, size_t needsize)
{
	char *s = *ps;

	uint64_t len = ds_len(s) + needsize;
	uint64_t cap = ds_cap(s);

	// 空间足够,无需扩容
	if (len <= cap)
		return s;

	// 根据数据大小,决定预留多少空间
	if (len > 128*1024*1024)
	    // len 大于 128M,则预留 32M
		cap = len + 32*1024*1024;
	else if (len > 4*1024*1024)
		// len 在 (4M,128M] 之间,则根据平方根函数预留
		// 从预留 len*2 逐渐过渡到预留 len/4
		// 从 2M 增长到 128M 会增长 8 次
		// len 超过 255M,则 FastSqrt 可能溢出
		cap = len + fast_sqrt(len * 8) * 1024;
	else if (len < 16)
		// 最少 16 字节
		cap = 16;
	else
		// 小于 4M,翻倍预留
		cap = len * 2;

	return ds_resize(ps, cap);
}


/* ------------------------------------------------------------
   resize dynamic string capacity
   功能:调整动态字符串的容量(会自动在容量后面添加 '\0')
   参数:动态字符串变量的地址,新的容量
   返回:动态字符串变量
------------------------------------------------------------ */
char * ds_resize(char **ps, size_t cap)
{
	char *s = *ps;

	// 包含尾部的 '\0' 位置
	s = realloc(s - DS_HEADER_SIZE, DS_HEADER_SIZE + cap + 1);
	if (s == NULL)
	{
		perror("resizeds()->realloc()");
		exit(1);
	}
	// 指向数据区
	s += DS_HEADER_SIZE;

	// 如果调整后的容量小于原来的字符串长度,则修正
	if (ds_len(s) > cap)
		// 修正长度并补上 \0
		ds_set_len(s, cap);

    // 修正容量并补上 \0
	ds_set_cap(s, cap);

	*ps = s;

	return s;
}


/* ------------------------------------------------------------
   pack dynamic string
   功能:压缩动态字符串(释放预留空间,只保留数据部分)
   参数:动态字符串变量的地址
   返回:动态字符串变量
------------------------------------------------------------ */
char * ds_pack(char **ps)
{
	return ds_resize(ps, ds_len(*ps));
}


/* ------------------------------------------------------------
   dynamic string add char
   功能:向动态字符串中追加字符
   参数:动态字符串变量的地址,要写入的字符
   返回:动态字符串变量
------------------------------------------------------------ */
char * ds_add_char(char **ps, char c)
{
    ds_grow(ps, 1);

    char *s = *ps;

    ds_end(s)[0] = c;
    // 修正长度并补上 \0
    ds_set_len(s, ds_len(s) + 1);

    return s;
}

/* ------------------------------------------------------------
   dynamic string add string
   功能:向动态字符串中追加字符串
   参数:动态字符串变量的地址,要写入的字符串
   返回:动态字符串变量
------------------------------------------------------------ */
char * ds_add_str(char **ps, const char* sub)
{
	size_t len = strlen(sub);
    ds_grow(ps, len);

    char *s = *ps;

    memcpy(ds_end(s), sub, len);
    // 修正长度并补上 \0
    ds_set_len(s, ds_len(s) + len);

    return s;
}


/* ------------------------------------------------------------
   dynamic string add strings
   功能:向动态字符串中追加多个字符串
   参数:动态字符串变量的地址,要写入的字符串个数,字符串列表
   返回:动态字符串变量
------------------------------------------------------------ */
char * ds_add_strs(char **ps, int count, ...)
{
    va_list valist;

    va_start(valist, count);

    for (int i=0; i<count; i++) {
        const char *sub = va_arg(valist, char *);
        ds_add_str(ps, sub);
    }

    va_end(valist);

    return *ps;
}


/* ------------------------------------------------------------
   dynamic string insert numbered string
   功能:在动态字符串的指定位置插入指定长度的子串
   参数:插入点、要插入的子串、子串长度(0 表示全部)
   返回:动态字符串变量
------------------------------------------------------------ */
char * ds_ins_str(char **ps, size_t pos, const char *sub, size_t len)
{
    char *s = *ps;

    if (pos > ds_len(s))
		pos = ds_len(s);

    if (len == 0)
		len = strlen(sub);

    ds_grow(ps, len);
    s = *ps;

    // 腾出空间
    memcpy(s + pos + len, s + pos, ds_len(s) - pos);
    // 插入子串
    memcpy(s + pos, sub, len);
    // 修正长度并补上 \0
    ds_set_len(s, ds_len(s) + len);

    return s;
}
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值