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

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

前言

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


函数模拟实现

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从最后向前一个字节,继续赋值……,直到需要移动的字节移动完毕停止。


总结

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

相关文章
|
9天前
|
人工智能 JSON 安全
Fastjson远程代码执行漏洞,阿里云AI安全为您保驾护航
阿里云AI安全产品联动防御Fastjson攻击
2317 12
Fastjson远程代码执行漏洞,阿里云AI安全为您保驾护航
|
9天前
|
云安全 人工智能 安全
|
9天前
|
人工智能 自然语言处理 数据挖掘
Qwen3.8-Max-Preview深度全解析:2.4万亿参数旗舰MoE模型+Token Plan限时优惠完整落地指南
2026年7月,全新旗舰级混合专家大模型Qwen3.8-Max-Preview正式开放抢先体验,作为通义千问Qwen3系列规格最高、综合推理能力顶尖的新一代模型,该模型总参数量达到2.4万亿(2.4T),是当前线上可调用的原生多模态旗舰模型,综合推理水准对标海外顶级Fable 5模型,在复杂工程开发、长文档深度分析、多步骤智能体自治、跨境多语言创作、海量数据挖掘五大高难度业务场景实现跨越式性能提升。
1035 2
|
9天前
|
人工智能 自然语言处理 数据挖掘
最新版通义千问(Qwen3.8-Max-Preview)功能介绍
2026年,通义千问正式推出全新旗舰级大模型 **Qwen3.8-Max-Preview 预览版**,作为首款突破万亿参数规格的新一代基座模型,该模型总参数量达到**2.4万亿**,采用全新迭代的MoE混合专家架构,综合推理性能、长文本处理、多模态理解、复杂任务规划能力全面超越前代Qwen3.7-Max版本,整体实力跻身全球第一梯队,可对标海外顶级旗舰模型,是当前面向复杂工程开发、多智能体协同、超长文档解析、专业办公自动化场景的最优国产基座模型。
1079 0
|
11天前
|
人工智能
Qwen3.8抢先体验!正式版即将发布并开源!
千问Qwen3.8即将开源,参数达2.4T,进化速度以“天”计,实力媲美Fable 5。预览版Qwen3.8-Max已上线阿里Token Plan等平台,限时优惠:日间Credits低至1折,夜间更优,个人/团队版月付仅35元起!
1040 44
|
7天前
|
自然语言处理 测试技术 API
通义千问Qwen3.8-Max-Preview全功能解析:2.4万亿参数旗舰模型深度使用指南
在大模型技术持续迭代的当下,通义千问推出的Qwen3.8-Max-Preview作为新一代旗舰预览版模型,凭借2.4万亿参数的超大规模、多模态融合能力与全场景适配特性,成为开发者与企业用户探索AI应用的核心工具。该模型采用稀疏混合专家(MoE)架构,是通义千问首个突破万亿参数的多模态模型,可同时处理文本、图像、视频与文档等多种数据形态,在全栈代码开发、复杂逻辑推理、长文档分析与多智能体协作等场景实现跨越式升级。本文将全面拆解Qwen3.8-Max-Preview的核心功能,详解API调用流程与配置方法,覆盖多场景实战技巧,帮助用户快速掌握这款旗舰模型的使用方法,充分释放其性能潜力。
511 1
|
7天前
|
人工智能 前端开发 Linux
Codex 桌面版安装 + CC Switch 接入第三方 API 完整教程(2026 最新)
2026最新教程:手把手教你安装Codex桌面版,通过CC Switch v3.17.0一键接入Fenno等国产API(兼容OpenAI Responses格式),跳过账号登录,完整启用代码审查、多步任务与上下文感知功能。零基础友好,全程图文实操。(239字)
595 0
|
10天前
|
人工智能 自然语言处理 数据挖掘
Qwen3.8-Max 预览版全解析:2.4 万亿参数旗舰模型,Token Plan 限时优惠指南
Qwen3.8-Max-Preview是通义千问Qwen3系列旗舰MoE大模型,参数达2.4万亿,综合推理能力居行业第一梯队。支持思考/快速双模式,擅长大模型五大高难场景。现于阿里云百炼Token Plan、Qoder及QoderWork上线体验,个人版低至39元/月。在阿里云百炼官网:https://t.aliyun.com/U/fPVHqY 免费领取千万Tokens
709 1
Qwen3.8-Max 预览版全解析:2.4 万亿参数旗舰模型,Token Plan 限时优惠指南