探索字符与字符串:基本库函数的使用(二)

简介: 探索字符与字符串:基本库函数的使用(二)

前言

继接上文,本片文章我将带领大家去模拟实现一些基本的库函数。


函数模拟实现

strlen

前文我们已将基本了解了strlen函数是用于计算字符串长度的,那么接下来我们来模拟实现一下该函数

strlen函数统计的是\0之前的字符个数,函数返回类型是int,这里我们有三种实现方法:

方法一:计数器

int my_strlen(const char *ch)
{
  int i = 0;
  while(*ch != '\0') {
    ch++;
    i++;
  }
  return i;
}

方法二:递归

int my_strlen(const char * str)
{
if(*str == '\0')
return 0;
else
return 1+my_strlen(str+1);
}

方法三:指针运算

int my_strlen(char *s)
{
char *p = s;
while(*p != ‘\0’ )
p++;
return p-s;
}

这些方法的代码实现都较为简单,就不再细介绍。

strcpy

在模拟实现strcpy函数时需要注意以下几点:

  • 函数参数顺序
  • 函数的功能,停止条件
  • assert
  • const修饰指针
  • 函数返回值

我们再来看一下函数的原型:

char* strcpy(char * destination, const char * source );

注意const修饰的参数模拟实现代码:

char *my_strcpy(char* dest, const char* src)
{
  assert(src&&dest);
  char* ret = dest;
  while (*dest++ = *src++)
  {
    ;
  }
  return ret;
}

assert断言,为了防止传进来为NULL,后置++的优先级高于*,所以在*dest++中先后置++,再解引用。当赋值到\0时循环就会结束,返回目标字符串的起始位置。

strcat

函数的原型与strcpy函数原型类似。

strcat函数的功能是连接两个字符串,我们可以依据strcpy的思路将源字符串的内容copy到目标字符串的末尾,我们只需找到目标字符串中\0的位置即可

代码实现:

char *my_strcat(char *dest, const char*src)
{
char *ret = dest;
assert(dest != NULL);
assert(src != NULL);
while(*dest)
{
dest++;
}
while((*dest++ = *src++))
{
;
}
return ret;
}

那为什么strcat最好不要自己给自己追加。

看到模拟实现的strcat我们或许就会发现问题:

如果自己给自己追加,初始时dest和src就指向同一个字符串,当dest 向后移动找到\0以后就会停止(需要追加的位置),然后就会开始追加,将src指向的字符赋给dest指向的位置,这就会把\0给替换掉:

但是\0对于src来说又很重要,这是循环结束的标志,而此时\0被替换,循环就无法停止,便会一直向后赋值,知道越界访问程序崩溃停止。

strstr

strstr函数的功能是在一个字符串中查找指定的子串,并返回该子串在原字符串中的起始位置。

函数原型:

char* strstr(const char* str1, const char* str2);

strstr在不依靠库函数的情况下,对其模拟实现可能有点复杂。

具体代码:

char* my_strstr(char* str1, char* str2)
{
  char* s1 = str1;
  char* s2 = str2;
  char* cp = str1;
  while (*cp)
  {
    s1 = cp;
    s2 = str2;
    while (*s1 && * s2 && *s1 == *s2)
    {
      s1++;
      s2++;
    }
    if (*s2 == '\0')
      return cp;
    cp++;
  }
  return NULL;
}

以a b b b c d e f和b b c为例:

初始时两指针指向两字符串的首元素地址,问题在于要确定开始匹配的位置,那么我们就需要一个指针来存放开始匹配的位置,我们设为cp,初始阶段开始匹配,cp指向的位置和str1相同。

cp不断的向后寻找开始匹配的位置,如果*cp=\0,就说明cp遍历了整个字符串都没有找到匹配点,说明str2不属于str1的子字符串,返回NULL。

以此为前提才能开始匹配。从第一个字符开始向后匹配,匹配失败后我们就需要移动cp向后移动尝试从下一个位置开始,所以进行cp++;

s1和s2是进行字符比对的指针,起初s1和s2都是从字符串的首元素开始:

然后s1和s2同步进行移动,对字符进行一一比对,

while (*s1 == *s2)
    {
      s1++;
      s2++;
    }

如果匹配失败,cp就向后移动,s1从cp指向的位置开始继续比对(s1=cp),s2从str2指向的字符串的首元素开始(s2=str2),如果匹配成功,此时的s2指向的一定是字符串末尾的\0由此我们就可以以此为判定条件,判断是否匹配成功:

while (*s1 && * s2 && *s1 == *s2)
    {
      s1++;
      s2++;
    }
    if (*s2 == '\0')
      return cp;

匹配成功就可以提前结束程序,返回首次匹配成功的初始位置(return cp)。遍历结束都没有找到则返回空指针。

strcmp

strcmp函数的功能是比较两字符串大小。

具体功能:

  • 比较两个字符串的大小。
  • 返回一个整数值,表示两个字符串的大小关系。
  • 如果字符串相等,返回值为0。
  • 如果字符串1大于字符串2,返回值大于0。
  • 如果字符串1小于字符串2,返回值小于0

由此我们来模拟实现一下,首先我们依次比对两个字符串对应位置字符的大小。如果有一个遍历结束,就返回0。但切记while循环里不可以这样写*str1++ == *str2++。

如果str1和str2不相等退出循环后仍会向后走一步,这样就跳过了对应位置上第一个不相等的字符,就会导致在判断大小时出现错误。

所以我们选择在循环里进行++操作。

在两字符不相等的前提下,如果str1指向的字符大于str2指向的字符,就返回1,否则就返回-1

int  my_strcmp(const char* str1,const char* str2)
{
  assert(str1 && str2);
  while (*str1 == *str2)
  {
    if (*str1 == '\0')
      return 0;
    str1++;
    str2++;
  }
  if (*str1 > *str2)
    return 1;
  else
    return -1;

memcpy

函数原型:

void * memcpy ( void * destination, const void * source, size_t num );

从source的位置开始向后复制num个字节的数据到destination的内存位置。

此外我们还要注意:

  • 这个函数在遇到 '\0' 的时候并不会停下来。
  • 如果source和destination有任何的重叠,复制的结果都是未定义的。

这里注意,memcpy的函数类型是void* 类型,这表明memcpy函数可以用于所以类型的数据。

那么在模拟实现时,我们就要设计一个可以适用于任何类型的函数。最终的返回结果是目标dest数据的起始地址。

于是我们就可以这样设计

模拟代码演示:

void* my_memcpy(void* dest, void* src, size_t num)
{
  void* ret = dest;
  assert(dest && src);
  while (num--)
  {
    *(char*)dest = *(char*)src;
    dest = (char*)dest + 1;
    src = (char*)src + 1;
  }
  return ret;
}

num为需要copy的字节大小,可以作为循环的次数,不管传入任何类型的数据,我们都将其转换为char*类型,进行一个字节一个字节的复制。最终返回目标dest数据的起始地址。只需创建一个变量存储即可。

memmove

函数原型:

void* memmove(void* dest, const void* src, size_t n);

dest是目标内存的起始地址,src是源内存的起始地址,n是要移动的字节数

注意:函数原型中的参数类型都是void*,表示传入的参数是任意类型的指针,可以用于处理不同类型的数据。返回值类型是void*,表示返回一个指向目标内存的指针。

那么我们要如何设计呢?

有人可能会想,这个简单,我们模仿上边的memcpy函数进行复制就好了,但这样真的可以吗?

在模拟的memcpy中确实可以实现部分的数据移动,但它并不完善。这里我们需要考虑到数据覆盖问题。

如果是将数据从高地址移动到同一数组的低地址处,这样可以实现如:

memcpy(arr, arr + 2, 20);

但如果是将前边的数据向后移动呢?

memcpy(arr+2, arr , 20);

当我们想把arr处20个字节的数据向后移动2个单位时,1赋值给了3,2赋值给了4,那到3赋值时3的值就已经被覆盖,这就会导致最终结果错误。

那我们应该怎么解决呢?

这时我们就可以考虑从源数据段的最后边开始赋值,20个字节,5个元素,先将5赋给7,4赋给6,3赋给5……

 

这样就可以实现向后移动。

回到memmove函数,它是一个可以接收任何类型数据的函数,那么次时我们就需要将数据类型转换为char*类型,进行一个字节一个字节的赋值。

代码实现:

my_memmove(void* dest, const void* src, size_t n)
{
  if (dest < src)
  {
    while (n--)
    {
      *(char*)dest = *(char*)src;
      dest = (char*)dest + 1;
      src = (char*)src + 1;
    }
  }
  else
  {
    while (n--)
    {
      *((char*)dest+n) = *((char*)src+n);
    }
  }
}

根据src和dest的大小进行判断,选择合适的方法。

(char*)dest+n和(char*)src+n将两个指针移动到需要后移的数据最后,从后向前依次赋值。每移动一个字节,n--,dest和src从最后向前一个字节,继续赋值……,直到需要移动的字节移动完毕停止。


总结

好的本期内容到此结束,模拟实现这些内容虽然很不常用,但是却可以帮助我们更好的理解和正确的使用这些库函数,同样也可以帮助我们提升一定的算法能力,对于处理一些字符操作题目很有帮助。最后,感谢阅读!

相关文章
|
8天前
|
人工智能 自然语言处理 安全
阿里云千问办公 QwenWork详细介绍:产品核心能力、典型场景、价格及常见问题解答
千问办公是阿里云推出的一站式AI办公平台,主打"不止于对话,更注重交付",依托通义千问旗舰大模型,用户一句话即可完成数据分析、PPT生成、视频剪辑等复杂任务,直接输出可用成果。产品深度打通钉钉生态与企业OA,覆盖桌面端、网页端,提供企业标准版198元/人/月等多档订阅方案,新用户注册即赠2000积分,适配工程师、HR、财务等多职业办公场景,成为能动手干活的"全能AI同事"。
|
8天前
|
人工智能 API 内存技术
刚刚 DeepSeek V4.1 Flash 开启内测,1 分钟教你用上!
刚刚 DeepSeek 内测群发布了 DeepSeek V4.1 Flash 中间版本内测的消息,这次的模型采用了新的结构,原生支持多模态、能力更强、速度更快、且成本更低。
1851 15
|
14天前
|
网络协议 Linux iOS开发
【2026实测】Wireshark下载+安装+汉化+使用教程(图文版,巨详细)
Wireshark 是一款免费开源的网络协议分析工具,可实时捕获、解析并可视化数据包,助你诊断网络故障、分析通信协议(如HTTP、DNS、TCP等)。支持Windows/macOS/Linux,含中文界面,新手入门便捷。(239字)
|
8天前
|
人工智能
千问办公官网入口:阿里AI办公QwenWork产品页和免费网页端链接
千问办公官网含两大入口:一是网页端(qwenwork.cn),即开即用,支持浏览器直接访问;二是阿里云产品页 https://t.aliyun.com/U/JNKJuO 提供免费/付费版详情、功能介绍及使用指南。
|
13天前
|
人工智能 运维 BI
阿里云千问办公QwenWork深度解析:基于Qwen3.8,六大核心能力重构企业全自动化工作流与计费选型指南
传统AI办公工具大多停留在对话问答、文档摘要、简单文案生成层面,只能完成单点碎片化任务,无法自主拆解复杂业务流程,很难串联多工具、多文档、外部业务系统完成端到端完整工作交付。很多企业在落地AI办公的时候,需要组合多款不同工具,来回切换界面,手动复制粘贴中间结果,智能化改造落地门槛居高不下。千问办公QwenWork是整合多款智能体产品能力打造的一体化企业办公智能体平台,底层基座依托Qwen3.8大模型,打通桌面端Agent、云端Agent、企业协同Agent三种运行形态,不再局限简单问答,接收业务目标之后自主拆解任务步骤,调用各类工具,处理文档、表格、浏览器自动化、数据查询,直接输出可交付的办公
1661 3
|
7天前
|
IDE 开发工具
Qoder 上线 Sonus 模型,Computer Use 能力全面增强
Qoder国际版上线全新内置大模型Sonus(/ˈsoʊnəs/),全球领先,专精超长任务执行与电脑操作(Computer Use)。配合Qoder桌面端0.2.3版本,可自主完成编程、金融建模、科研及表格制作等复杂工作。现全面支持Qoder全系产品,效率提升3.2倍。
864 1
Qoder 上线 Sonus 模型,Computer Use 能力全面增强
|
10天前
|
SQL 人工智能 前端开发
QoderWake 1.0 正式发布:从桌面里的 Agent,到工作现场的数字员工
QoderWake v1.0正式发布:企业级数字员工团队平台。支持“一句话建岗”,预置10类特训岗位;Waker常驻钉钉/飞书群,@即响应、自动协作、跨任务记忆;具备定时/事件/API多触发方式与统一任务看板;已沉淀27.6万条记忆、12.3万项技能,助力组织实现人机协同增效。
804 2
|
14天前
|
缓存 数据可视化 开发工具
DeepSeek Harness 怎么更新?dsh 更新完整指南:更新本体(npx、npm、源码)与更新插件两种方式
DeepSeek Harness 的更新分两层:本体更新(npx 自动最新、npm update -g、源码 git pull)与插件更新(插件市场点更新、命令行覆盖安装)。本文按「准备 → 更新本体 → 更新插件 → 更新后检查」四步走,覆盖新手常见疑问。
1709 1
DeepSeek Harness 怎么更新?dsh 更新完整指南:更新本体(npx、npm、源码)与更新插件两种方式
|
7天前
|
缓存 测试技术 API
DeepSeek V4.1 Flash 内测接入:改个模型名即可调用(附代码)
DeepSeek V4.1 Flash 内测不用申请,base_url 不变、改个模型名就能调,9/10 到期。本文讲清接入、计费限流与多模态注意点。
819 0
DeepSeek V4.1 Flash 内测接入:改个模型名即可调用(附代码)
|
9天前
|
缓存 人工智能 自然语言处理
阿里云qwen3.8-flash大模型介绍:模型能力、模型价格、免费额度与最新活动
本文是阿里云百炼平台Qwen3.8-Flash大模型的选型接入指南,作为兼顾性能与响应速度的高性价比多模态模型,它支持百万级上下文窗口、全场景多模态输入与完整智能体能力矩阵,适配编程辅助、智能体协作等核心场景。文中同步梳理了最新下调的阶梯定价、夜间4折等优惠活动,搭配OpenAI兼容流式调用示例,帮助开发者低成本快速落地高并发AI应用。
阿里云qwen3.8-flash大模型介绍:模型能力、模型价格、免费额度与最新活动