C语言:优雅的字符串函数库

嵌入式ARM 2020-08-31 00:00


一、沉浸式学习

以学习一门语言为例:
大多数人都持有一种观念,要真正学好一门语言必须得去所学语言当地学习或生活一段时间。

而事实上,大多数人都没有这样的学习条件。

解决问题的方法是:
自行改造环境,为自己创造沉浸式的学习环境。

例如:

  • 看新语言的电影;

  • 更改手机、电脑的语言设置;

  • 看新语言的文档和书籍;

  • 用新语言写 todo list;

  • 用新语言来学习自己需要的专业知识;

  • 翻译新语言的文档并分享;

  • 逛所学语言的论坛和网站;

  • 用新语言写代码注释 / commit message / README / issue;

  • ...

对了,我作为英文的爱好者,一直想重启我的英文学习之路,后续想在公众号里记录一些英文相关的知识,请你们不要笑话我~~~


二、字符串函数库:Simple Dynamic Strings

1. 简介

Simple Dynamic Strings (简称 SDS) 是一个 C 语言字符串库,它增强了 C 语言字符串处理的能力。

设计 SDS 原本是为了满足设计者自身日常的 C 编程,后来又被转移到 Redis 中,在 Redis 中被广泛使用并对其进行了修改以适合于高性能操作。现在,它又被从 Redis 中提取出来的,并 fork 为一个独立项目。

只有 1500 行不到的代码,就能做到 3.2K 个 star,牛牛牛~~~

它有什么优点?

  • 使用更简单;

  • 二进制安全;

  • 效率更高;

  • 与 C 字符串函数兼容;

源码链接:

https://github.com/antirez/sds

源码文件:

sds.c
sdsalloc.h
sds.h
testhelp.h

相关 API:

sds sdsnewlen(const void *init, size_t initlen) 
sds sdsempty(void) 
sds sdsnew(const char *init) 
sds sdsdup(const sds s) 
void sdsfree(sds s) 
void sdsupdatelen(sds s) 
void sdsclear(sds s) 
sds sdsMakeRoomFor(sds s, size_t addlen) 
sds sdsRemoveFreeSpace(sds s) 
size_t sdsAllocSize(sds s) 
void *sdsAllocPtr(sds s) 
void sdsIncrLen(sds s, ssize_t incr) 
sds sdsgrowzero(sds s, size_t len) 
sds sdscatlen(sds s, const void *t, size_t len) 
sds sdscat(sds s, const char *t) 
sds sdscatsds(sds s, const sds t) 
sds sdscpylen(sds s, const char *t, size_t len) 
sds sdscpy(sds s, const char *t) 
int sdsll2str(char *s, long long value) 
int sdsull2str(char *s, unsigned long long v) 
sds sdsfromlonglong(long long value) 
sds sdscatvprintf(sds s, const char *fmt, va_list ap) 
sds sdscatprintf(sds s, const char *fmt, ...) 
sds sdscatfmt(sds s, char const *fmt, ...) 
sds sdstrim(sds s, const char *cset) 
void sdsrange(sds s, ssize_t start, ssize_t end) 
void sdstolower(sds s) 
void sdstoupper(sds s) 
int sdscmp(const sds s1, const sds s2) 
sds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count) 
void sdsfreesplitres(sds *tokens, int count) 
sds sdscatrepr(sds s, const char *p, size_t len) 
int is_hex_digit(char c) 
int hex_digit_to_int(char c) 
sds *sdssplitargs(const char *line, int *argc) 
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) 
sds sdsjoin(char **argv, int argc, char *sep) 
sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen)

2. 比较常用的功能

2.1 创建字符串

sdsnew() 和 sdsfree():

#include <stdio.h>
#include "sds.h"
#include "sdsalloc.h"

int main(void)
{
    sds mystr = sdsnew("Hello World!");
    printf("%s\n", mystr);
    sdsfree(mystr);
}

运行效果:

$ gcc -o sdsdemo sds.c sdsdemo.c
$ ./sdsdemo
Hello World!

看到了吗?

printf 直接就可以打印 sds,这就是说 sds 本身就是 C 语言的字符串类型

sds 的定义如下:

typedef char *sds;

也就是说,sds 是能兼容 libc 里字符串处理函数 (例如strcpy, strcat...)的。

当不再使用 sds 字符串时,就算是空串,也要通过 sdsfree 销毁字符串。

2.2 获取字符串长度

sdsnewlen():

int main(void)
{
    char buf[3];
    sds mystring;

    buf[0] = 'A';
    buf[1] = 'B';
    buf[2] = 'C';
    mystring = sdsnewlen(buf,3);
    printf("%s of len %d\n", mystring, (int) sdslen(mystring));
}

运行效果:

$ ./sdsdemo
ABC of len 3

和 strlen() 有 2 点不同

  • 运行时长固定,sds 内部有数据结构保存着字符串的长度;

  • 长度与字符串内是否有 NULL 无关;

2.3 拼接字符串

sdscat():

int main(void)
{
    sds s = sdsempty();
    s = sdscat(s, "Hello ");
    s = sdscat(s, "World!");
    printf("%s\n", s);
}

运行效果:

$ ./sdsdemo
Hello World!

sdscat 接受的参数是以 NULL 结尾的字符串,如果想摆脱这个限制,可以用 sdscatsds()。

sdscatsds():

int main(void)
{
    sds s1 = sdsnew("aaa");
    sds s2 = sdsnew("bbb");
    s1 = sdscatsds(s1,s2);
    sdsfree(s2);
    printf("%s\n", s1);
}

运行效果:

$ ./sdsdemo
aaabbb

2.4 扩展字符串长度

sdsgrowzero():

int main(void)
{
    sds s = sdsnew("Hello");
    s = sdsgrowzero(s,6);
    s[5] = '!'/* We are sure this is safe*/
    printf("%s\n", s);
}

运行效果:

$ ./sdsdemo
Hello!

2.5 格式化字符串

sdscatprintf():

int main(void)
{
    sds s;
    int a = 10, b = 20;
    s = sdsnew("The sum is: ");
    s = sdscatprintf(s,"%d+%d = %d",a,b,a+b);
    printf("%s\n", s);
}

运行效果:

$ ./sdsdemo
The sum is: 10+20 = 30

2.6 截取字符串

sdstrim():去掉指定字符

int main(void)
{
    sds s = sdsnew("         my string\n\n  ");
    sdstrim(s," \n");
    printf("-%s-\n",s);
}

运行效果:

$ ./sdsdemo
-my string-

去掉了空格和换行符。

sdsrange():截取指定范围内的字符串

int main(void)
{
    sds s = sdsnew("Hello World!");
    sdsrange(s,1,4);
    printf("-%s-\n", s);
}

运行效果:

$ ./sdsdemo
-ello-

2.7 字符串分割 (Tokenization)

sdssplitlen() 和 sdsfreesplitres():

int main(void)
{
    sds *tokens;
    int count, j;

    sds line = sdsnew("Hello World!");
    tokens = sdssplitlen(line, sdslen(line)," ",1,&count);

    for (j = 0; j < count; j++)
        printf("%s\n", tokens[j]);
    sdsfreesplitres(tokens,count);
}

sdssplitlen() 第 3和4 个参数指定分割符为空格。

运行效果:

$ ./sdsdemo
Hello
World!

2.8 字符串合并 (String joining)

sdssplitlen() 和 sdsfreesplitres():

int main(void)
{
    char *tokens[3] = {"foo","bar","zap"};
    sds s = sdsjoin(tokens, 3"|");
    printf("%s\n", s);
}

运行效果:

$ ./sdsdemo
foo|bar|zap

还有其他一些功能,用到再研究吧!

3. 简单了解一下内部实现

在 SDSD 中,使用二进制前缀(头部) 来保存字符串相关的信息,该头部存储在 SDS 返回给用户的字符串的实际指针之前:

+--------+-------------------------------+-----------+
| Header | Binary safe C alike string... | Null term |
+--------+-------------------------------+-----------+
         |
         `-> Pointer returned to the user.

这个 Header 在代码中用结构体来描述,该结构体定义大致如下:

struct sdshdr {
    [...]
    int len;
    char buf[];
};
  • len 存储的是字符串长度;

  • buf 指向紧随其后的字符串首地址;

假设你使用的字符串为 "HELLOWORLD",为了提升效率,SDS 可能会提前分配多一些空间,所以实际的内存布局如下:

+------------+------------------------+-----------+---------------\
| len | buf | H E L L O W O R L D \n | Null term |  Free space   \
+------------+------------------------+-----------+---------------\
             |
             `-> Pointer returned to the user.

现在,我们来看一下 SDS 分配字符串的大致步骤:

sds sdsnew(const char *init)
    initlen 
= (init == NULL) ? 0 : strlen(init);
    sdsnewlen(init, initlen);
        int hdrlen = sdsHdrSize(type);      // 确定 Header 的长度
        sh = s_malloc(hdrlen+initlen+1);    // 分配 Header + String + 1 个字节的空间

        s = (char*)sh+hdrlen;       // 保存 C string 的地址
        SDS_HDR_VAR(8,s);           // 定义 struct sdshdr sh
        sh->len = initlen;          // 初始化 struct sdshdr sh

        if (initlen && init)        // 初始化 C string 
            memcpy(s, init, initlen);
        
        s[initlen] = '\0';          // 总是添加一个 NULL
        return s;                   // 返回 C string

其他的 SDS API 是如何实现的,就留给大家自行分析了。

4. 相关参考

-《Linux程序设计》,6,7.1 章节

-《C primer plus》,11,12 章节

-《C 和指针》,9 章节

-《Linux 系统编程》,9 章节

-《C专家编程》,7.5 章节

-《C和C++程序员面试秘笈》,4 章节


-END-


本文授权转载自嵌入式Hacker,作者:吴伟东Jack




推荐阅读



【01】2020年顶级编程语言
【02】国内为什么写不出操作系统和编程语言?
【03】单片机编程技术学习攻略
【04】终于有人讲清楚了!关于编程语言的那些事儿
【05】编程能力的4种境界,你到哪一级了?


免责声明:整理文章为传播相关技术,版权归原作者所有,如有侵权,请联系删除
嵌入式ARM 关注这个时代最火的嵌入式ARM,你想知道的都在这里。
评论
  • 自动化已成为现代制造业的基石,而驱动隔离器作为关键组件,在提升效率、精度和可靠性方面起到了不可或缺的作用。随着工业技术不断革新,驱动隔离器正助力自动化生产设备适应新兴趋势,并推动行业未来的发展。本文将探讨自动化的核心趋势及驱动隔离器在其中的重要角色。自动化领域的新兴趋势智能工厂的崛起智能工厂已成为自动化生产的新标杆。通过结合物联网(IoT)、人工智能(AI)和机器学习(ML),智能工厂实现了实时监控和动态决策。驱动隔离器在其中至关重要,它确保了传感器、执行器和控制单元之间的信号完整性,同时提供高
    腾恩科技-彭工 2025-01-03 16:28 161浏览
  • PLC组态方式主要有三种,每种都有其独特的特点和适用场景。下面来简单说说: 1. 硬件组态   定义:硬件组态指的是选择适合的PLC型号、I/O模块、通信模块等硬件组件,并按照实际需求进行连接和配置。    灵活性:这种方式允许用户根据项目需求自由搭配硬件组件,具有较高的灵活性。    成本:可能需要额外的硬件购买成本,适用于对系统性能和扩展性有较高要求的场合。 2. 软件组态   定义:软件组态主要是通过PLC
    丙丁先生 2025-01-06 09:23 29浏览
  • 在测试XTS时会遇到修改产品属性、SElinux权限、等一些内容,修改源码再编译很费时。今天为大家介绍一个便捷的方法,让OpenHarmony通过挂载镜像来修改镜像内容!触觉智能Purple Pi OH鸿蒙开发板演示。搭载了瑞芯微RK3566四核处理器,树莓派卡片电脑设计,支持开源鸿蒙OpenHarmony3.2-5.0系统,适合鸿蒙开发入门学习。挂载镜像首先,将要修改内容的镜像传入虚拟机当中,并创建一个要挂载镜像的文件夹,如下图:之后通过挂载命令将system.img镜像挂载到sys
    Industio_触觉智能 2025-01-03 11:39 113浏览
  • 【工程师故事】+半年的经历依然忧伤,带着焦虑和绝望  对于一个企业来说,赚钱才是第一位的,对于一个人来说,赚钱也是第一位的。因为企业要活下去,因为个人也要活下去。企业打不了倒闭。个人还是要吃饭的。企业倒闭了,打不了从头再来。个人失业了,面对的不仅是房贷车贷和教育,还有找工作的焦虑。企业说,一个公司倒闭了,说明不了什么,这是正常的一个现象。个人说,一个中年男人失业了,面对的压力太大了,焦虑会摧毁你的一切。企业说,是个公司倒闭了,也不是什么大的问题,只不过是这些公司经营有问题吧。
    curton 2025-01-02 23:08 290浏览
  • 影像质量应用于多个不同领域,无论是在娱乐、医疗或工业应用中,高质量的影像都是决策的关键基础。清晰的影像不仅能提升观看体验,还能保证关键细节的准确传达,例如:在医学影像中,它对诊断结果有着直接的影响!不仅如此,影像质量还影响了:▶ 压缩技术▶ 存储需求▶ 传输效率随着技术进步,影像质量的标准不断提高,对于研究与开发领域,理解并提升影像质量已成为不可忽视的重要课题。在图像处理的过程中,硬件与软件除了各自扮演着不可或缺的基础角色,有效地协作能够确保图像处理过程既高效又具有优异的质量。软硬件各扮演了什么
    百佳泰测试实验室 2025-01-03 10:39 139浏览
  • 根据Global Info Research项目团队最新调研,预计2030年全球封闭式电机产值达到1425百万美元,2024-2030年期间年复合增长率CAGR为3.4%。 封闭式电机是一种电动机,其外壳设计为密闭结构,通常用于要求较高的防护等级的应用场合。封闭式电机可以有效防止外部灰尘、水分和其他污染物进入内部,从而保护电机的内部组件,延长其使用寿命。 环洋市场咨询机构出版的调研分析报告【全球封闭式电机行业总体规模、主要厂商及IPO上市调研报告,2025-2031】研究全球封闭式电机总体规
    GIRtina 2025-01-06 11:10 26浏览
  • 物联网(IoT)的快速发展彻底改变了从智能家居到工业自动化等各个行业。由于物联网系统需要高效、可靠且紧凑的组件来处理众多传感器、执行器和通信设备,国产固态继电器(SSR)已成为满足中国这些需求的关键解决方案。本文探讨了国产SSR如何满足物联网应用的需求,重点介绍了它们的优势、技术能力以及在现实场景中的应用。了解物联网中的固态继电器固态继电器是一种电子开关设备,它使用半导体而不是机械触点来控制负载。与传统的机械继电器不同,固态继电器具有以下优势:快速切换:确保精确快速的响应,这对于实时物联网系统至
    克里雅半导体科技 2025-01-03 16:11 165浏览
  • 车身域是指负责管理和控制汽车车身相关功能的一个功能域,在汽车域控系统中起着至关重要的作用。它涵盖了车门、车窗、车灯、雨刮器等各种与车身相关的功能模块。与汽车电子电气架构升级相一致,车身域发展亦可以划分为三个阶段,功能集成愈加丰富:第一阶段为分布式架构:对应BCM车身控制模块,包含灯光、雨刮、门窗等传统车身控制功能。第二阶段为域集中架构:对应BDC/CEM域控制器,在BCM基础上集成网关、PEPS等。第三阶段为SOA理念下的中央集中架构:VIU/ZCU区域控制器,在BDC/CEM基础上集成VCU、
    北汇信息 2025-01-03 16:01 173浏览
  •     为控制片内设备并且查询其工作状态,MCU内部总是有一组特殊功能寄存器(SFR,Special Function Register)。    使用Eclipse环境调试MCU程序时,可以利用 Peripheral Registers Viewer来查看SFR。这个小工具是怎样知道某个型号的MCU有怎样的寄存器定义呢?它使用一种描述性的文本文件——SVD文件。这个文件存储在下面红色字体的路径下。    例:南京沁恒  &n
    电子知识打边炉 2025-01-04 20:04 23浏览
  • 随着市场需求不断的变化,各行各业对CPU的要求越来越高,特别是近几年流行的 AIOT,为了有更好的用户体验,CPU的算力就要求更高了。今天为大家推荐由米尔基于瑞芯微RK3576处理器推出的MYC-LR3576核心板及开发板。关于RK3576处理器国产CPU,是这些年的骄傲,华为手机全国产化,国人一片呼声,再也不用卡脖子了。RK3576处理器,就是一款由国产是厂商瑞芯微,今年第二季推出的全新通用型的高性能SOC芯片,这款CPU到底有多么的高性能,下面看看它的几个特性:8核心6 TOPS超强算力双千
    米尔电子嵌入式 2025-01-03 17:04 13浏览
  • 光耦合器,也称为光隔离器,是一种利用光在两个隔离电路之间传输电信号的组件。在医疗领域,确保患者安全和设备可靠性至关重要。在众多有助于医疗设备安全性和效率的组件中,光耦合器起着至关重要的作用。这些紧凑型设备经常被忽视,但对于隔离高压和防止敏感医疗设备中的电气危害却是必不可少的。本文深入探讨了光耦合器的功能、其在医疗应用中的重要性以及其实际使用示例。什么是光耦合器?它通常由以下部分组成:LED(发光二极管):将电信号转换为光。光电探测器(例如光电晶体管):检测光并将其转换回电信号。这种布置确保输入和
    腾恩科技-彭工 2025-01-03 16:27 157浏览
  • 本文介绍Linux系统更换开机logo方法教程,通用RK3566、RK3568、RK3588、RK3576等开发板,触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。制作图片开机logo图片制作注意事项(1)图片必须为bmp格式;(2)图片大小不能大于4MB;(3)BMP位深最大是32,建议设置为8;(4)图片名称为logo.bmp和logo_kernel.bmp;开机
    Industio_触觉智能 2025-01-06 10:43 21浏览
  • 本文继续介绍Linux系统查看硬件配置及常用调试命令,方便开发者快速了解开发板硬件信息及进行相关调试。触觉智能RK3562开发板演示,搭载4核A53处理器,主频高达2.0GHz;内置独立1Tops算力NPU,可应用于物联网网关、平板电脑、智能家居、教育电子、工业显示与控制等行业。查看系统版本信息查看操作系统版本信息root@ido:/# cat /etc/*releaseDISTRIB_ID=UbuntuDISTRIB_RELEASE=20.04DISTRIB_CODENAME=focalDIS
    Industio_触觉智能 2025-01-03 11:37 138浏览
  • Matter加持:新世代串流装置如何改变智能家居体验?随着现在智能家庭快速成长,串流装置(Streaming Device,以下简称Streaming Device)除了提供更卓越的影音体验,越来越多厂商开始推出支持Matter标准的串流产品,使其能作为智能家庭中枢,连结多种智能家电。消费者可以透过Matter的功能执行多样化功能,例如:开关灯、控制窗帘、对讲机开门,以及操作所有支持Matter的智能家电。此外,再搭配语音遥控器与语音助理,打造出一个更加智能、便捷的居家生活。支持Matter协议
    百佳泰测试实验室 2025-01-03 10:29 143浏览
  • 在快速发展的能源领域,发电厂是发电的支柱,效率和安全性至关重要。在这种背景下,国产数字隔离器已成为现代化和优化发电厂运营的重要组成部分。本文探讨了这些设备在提高性能方面的重要性,同时展示了中国在生产可靠且具有成本效益的数字隔离器方面的进步。什么是数字隔离器?数字隔离器充当屏障,在电气上将系统的不同部分隔离开来,同时允许无缝数据传输。在发电厂中,它们保护敏感的控制电路免受高压尖峰的影响,确保准确的信号处理,并在恶劣条件下保持系统完整性。中国国产数字隔离器经历了重大创新,在许多方面达到甚至超过了全球
    克里雅半导体科技 2025-01-03 16:10 121浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦