组件:c语言版本字符串String

本文探讨了为何多数开发者选择C++而非C,关键在于C++内置的`std::string`提供了方便的字符串操作。作者分享了一个自定义的字符串实现,包括常量字符串、动态构建等功能,利用位运算优化存储,减少内存分配。同时,介绍了内存管理接口和数据结构,如`mmStringTiny`和`mmStringLong`,以及如何判断字符串类型和调整大小。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景介绍

为什么大多数人选择c++而不是c?我想可能是因为c标准里面没有方便使用的字符串"string"。虽然c标准确实不应该规定字符串实现,但是作为最终使用者的我们,还是迫切希望能有一份开箱即用,并且拥有现代品质的字符串。避免重造轮子的同时也能满足日常需要。

基本思路

// clang generally mode simplify string.
// https://github.com/llvm-mirror/libcxx/blob/master/include/string
// https://joellaity.com/2020/01/31/string.html

1.最终使用案例

接口参照std::string,以下为节选测试用例。
我拷贝替换增加了Utf16String,Utf32String,它们裁剪掉了string的printf部分。

https://bitbucket.org/mm_longcheng/mm-core/src/dev/mm/src/core/mmString.h

// 编译构建常量字符串,存储在全局区,没有堆分配
const struct mmString c = mmString_Make("abc");
const char* cs = mmString_Data(&c);
size_t cn = mmString_Size(&c);
printf("cs: %s %d\n", cs, (int)cn);

// 动态构建常量字符串,存储在局部区,没有堆分配
struct mmString w;
mmString_MakeWeak(&w, "123");
// 能正常重置,便于重新定义成可能的动态串
mmString_Reset(&w);

// 动态构建普通字符串,存储在局部区,可能堆分配
// x64位 长度超过22就会触发堆分配
struct mmString s;
mmString_Init(&s);
mmString_Assigns(&s, "456");
mmString_Destroy(&s);

2.前置数据结构组件

// 我们需要大小端判定宏
#define MM_ENDIAN              <平台相关>
#define MM_ENDIAN_LITTLE 1
#define MM_ENDIAN_BIGGER 2
// 我们需要内存接口,简单重定义为c标准接口即可
// 重定义是为了预留可供替换的备选内存管理
#define mmMalloc        
#define mmRealloc       
#define mmFree          
#define mmMemset(buf, c, n)     
#define mmMemcpy(dst, src, n)   
#define mmMemmove(dst, src, n) 
#define mmMovemem(dst, src, n) 
#define mmMemcmp(s1, s2, n)  

3.主要数据结构

typedef char mmUtf8_t;

struct mmStringLong
{
    // capacity
    size_t capacity;
    // size
    size_t size;
    // data pointer
    char*  data;
};

#if (MM_ENDIAN == MM_ENDIAN_BIGGER)
// bigger endian
#define mmStringTinyMaskMacro 0x80
#define mmStringLongMaskMacro ~((size_t)(~((size_t)(0))) >> 1)
#else
// little endian
#define mmStringTinyMaskMacro 0x01
#define mmStringLongMaskMacro 0x1ul
#endif

enum
{
    mmStringLongCapacity = (sizeof(struct mmStringLong) - 1) / sizeof(char),
    mmStringTinyCapacity = mmStringLongCapacity > 2 ? mmStringLongCapacity : 2,
};

struct mmStringTiny
{
    union
    {
        // size
        unsigned char size;
        // lx
        char lx;
    };
    // data buffer
    char data[mmStringTinyCapacity];
};

union mmStringUlxx { struct mmStringLong __lxx; struct mmStringTiny __txx; };

enum { mmStringNWords = sizeof(union mmStringUlxx) / sizeof(size_t) };

struct mmStringRaws
{
    // raw words buffer
    size_t words[mmStringNWords];
};

struct mmString
{
    union
    {
        // long
        struct mmStringLong l;
        // tiny
        struct mmStringTiny s;
        // raws
        struct mmStringRaws r;
    };
};

4.主要处理函数

// 是否为长串
static mmInline mmBool_t mmString_IsLong(const struct mmString* p)
{
    return p->s.size & mmStringTinyMask;
}

// 这里仅截取小端的情况
// little endian
static mmInline void mmString_SetTinySize(struct mmString* p, size_t size)
{
    p->s.size = (unsigned char)(size << 1);
}
static mmInline size_t mmString_GetTinySize(const struct mmString* p)
{
    return p->s.size >> 1;
}

static mmInline void mmString_SetLongSize(struct mmString* p, size_t size)
{
    p->l.size = size;
}
static mmInline size_t mmString_GetLongSize(const struct mmString* p)
{
    return p->l.size;
}

// 构建弱串时,我们将其定义为长串,并且其容量为0
MM_EXPORT_DLL void mmString_MakeWeaksn(struct mmString* p, 
    const char* __s, size_t __n)
{
    // const reference long capacity is 0.
    p->l.capacity = mmStringLongMask;
    p->l.size = __n;
    p->l.data = (char*)__s;
}
// 我们需要支持弱分配的串,删除处理时需要将其排除
static void mmString_Deallocate(struct mmString* p)
{
    size_t __cap = mmString_GetLongCapacity(p);
    if (mmString_IsLong(p) && 0 != __cap)
    {
        // long mode and long capacity equal 0 is weak or const reference.
        char* __p = mmString_GetLongPointer(p);
        // __alloc_traits::deallocate(__alloc(), __p, __cap);
        mmUtf8_Deallocate(__p, __cap);
    }
}

5.原理

借用两张图,感谢llvm组织设计出了这么精巧的字符串

a.使用头部一位来划定长串还是短串,x64下字符串最长为2的63次幂-1,而不是64
b.长串会分配两份内存,一份在栈上,格式为data指向堆块
c.短串会复用栈上的数据块,一字节存储短串长度,23字节为存储,短串最长22字节

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值