字符串函数的介绍及模拟实现

简介: 字符串函数的介绍及模拟实现

0.前言


  • C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串 中或者 字符数组 中。
  • 字符串常量 适用于那些对它不做修改的字符串函数。


 在本篇文章里,我见给大家介绍字符串函数以及如何去模拟实现字符串函数。首先,我们先学习一下strlen函数。


1.求字符串长度函数strlen


size_t strlen ( const char * str );


  关于strlen函数,我们需要知道一下几点:


字符串以'\0' 为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包

含 '\0' )。

参数指向的字符串必须要以 '\0' 结束。

注意函数的返回值为size_t,是无符号的。

学会模拟实现strlen函数。


 这几点对我们学习strlen这个库函数是相当重要的,现在来依次介绍以上几点。


bb57195d556745d689df87fd3a3ef075.png


 上面的代码很好地将字符数组中的字符个数求了出来,那接下来我们看一下下面的代码。


d282cb6f3c9443d9b314f2c614dd6096.png


 可以看到,这两段代码其实是差不多的,但是输出结果却出现了这么大的差别。为什么呢?原因就是strlen函数返回的是在字符串中 '\0' 前面出现的字符个数,而上面代码中的数组arr的'\0'不知道在哪里出现。所以输出结果就是随机值。


 现在我们来学习第三点,这点非常的重要,我将通过下面的代码给大家讲解。


#include <stdio.h>
#include <string.h>
int main()
{
    if (strlen("abc") - strlen("abcdef") > 0)
        printf(">\n");
    else
        printf("<\n");
    return 0;
}


这段代码的输出结果会是什么呢?是:>


为什么呢?


 因为strlen()返回的是无符号整数,虽然3-6小于零,但是-3被解读成一个无符号数的话,将会是一个非常大的正数,所以输出结果为> 。


8065fb07dad44ef39581dd327dc0a085.png


模拟实现strlen函数


①指数器方法


  定义一个变量来充当计算器,统计字符的个数。


#include <stdio.h>
#include <string.h>
#include <assert.h>
int my_strlen(const char* str)
{
    assert(str != NULL);//断言str不等于空指针
    int count = 0;//计数器
    while (*str != '\0')
    {
        count++;
        str++;
    }
    return count;
}
int main()
{
    char arr[] = "abcdef";
    int len = my_strlen(arr);
    printf("len = %d\n", len);
    return 0;
}


②递归


#include <stdio.h>
#include <string.h>
#include <assert.h>
int my_strlen(const char* str)
{
    assert(str != NULL);//断言str不等于空指针
    if (*str != '\0')
        return 1 + my_strlen(str + 1);
    else
        return 0;
}
int main()
{
    char arr[] = "abcdef";
    int len = my_strlen(arr);
    printf("len = %d\n", len);
    return 0;
}


③指针-指针版本


#include <stdio.h>
#include <string.h>
#include <assert.h>
int my_strlen(const char* str)
{
    assert(str != NULL);//断言str不等于空指针
    char* begin = str;
    while (*str != '\0')
    {
        str++;
    }
    return str - begin;
}
int main()
{
    char arr[] = "abcdef";
    int len = my_strlen(arr);
    printf("len = %d\n", len);
    return 0;
}


 以上就是模拟实现strlen函数的三种方法了。模拟实现strlen函数时,我们也要注意几个点。第一,就是要对传过来的指针进行断言,确保它不是空指针;第二,就是用const修饰传过来的指针,因为我们不需要修改它的内容。


 现在我们可以使用我们自己定义的my_strlen函数来比较两个字符串的长度了,因为该函数的返回类型是int(有符号整型)。


fc0a6c86ab194096bfee9dd2e552b2b4.png


2.长度不受限制的字符串函数


2.1strcpy


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


7c9faa0494d9462dae4c6b7522da2ac4.png


 对于strcpy函数,我们需要注意一下几点:


  • 该字符串是字符串的拷贝。
  • 源字符串必须以 '\0' 结束。
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改。
  • 学会模拟实现strcpy函数


strcpy的正确用法


#include <stdio.h>
#include <string.h>
int main()
{
  char arr[20] = { 0 };
  strcpy(arr, "hello world");
  printf("%s", arr);
  return 0;
}

bf55714a0ee7494786c20da7bfc999f7.png


观察strcpy是否会将源字符串的 '\0' 拷贝到目标空间


#include <stdio.h>
#include <assert.h>
#include <string.h>
int main()
{
  char arr[20] = "#################";
  char* p = "hello world";
  strcpy(arr, p);
  printf('%s\n', arr);
  return 0;
}

dc5c1bdd8abc4a80a092fae84ae63af9.jpg55e1255a17f94b6490b2d7f7df710fcc.jpg


 对比上面的两个图片,就可以发现strcpy函数会将源字符串的 '\0' 拷贝到目标空间。


strcpy的错误示范


①原字符串没有以'\0'结尾

86d44c7e237c4d59b0843d062b30b8c9.png


解决方案:写程序的是要注意要给字符数组加上 '\0' ,如果没有 '\0' 的话,strcpy就会找 arr2 后面的位置有没有 '\0' ,然后造成越界访问。


②目标空间不能容纳原字符串的内容

c227a8f22d0a43fb8903844b8d90f9ef.png

 解决方案:确保目标空间足够大,能够容纳源字符串的内容。


③ 目标空间不可修改

7cb2c3931c254f9e84dc5b8e5815b32b.png


解决方案:目标空间不能是常量字符串,必须是字符数组。


模拟实现strcpy函数


#include <stdio.h>
#include <assert.h>
#include <string.h>
char* my_strcpy(char* dest, const char* src)
{
  assert(dest && src);
  char* ret = dest;
  while (*dest++ = *src++)
  {
    ;
  }
  return ret;
}
int main()
{
  char arr[20] = "#################";
  const char* p = "hello world";
  printf("%s\n", my_strcpy(arr, p));
  return 0;
}

765f2f7d428a425f8685abbe28a88409.png


为什么 my_strcpy 函数的返回值不设置为 void,而设置为char*。为什么呢?因为 my_strcpy 函数的返回值为 char* ,能够实现函数的链式访问(把一个函数的返回值作为另外一个函数的参数)。


2.2strcat


char * strcat ( char * destination, constchar * source );


15bd149e8c2240d5970a5ea8625acc51.png


0762a51cd2d849d592b853f47791f2e3.png


 对于strcat函数,我们需要注意一下几点:


  • 字符串必须以 '\0' 结束。
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改。
  • 目标中的'\0'被源字符串的第一个字符覆盖。
  • 学会模拟实现strcat函数。


代码示例


#include <stdio.h>
#include <string.h>
int main()
{
  char arr[20] = "hello";
  strcat(arr, " world");//字符串追加(连接)
  printf("%s\n", arr);
  return 0;
}


模拟实现strcat函数


#include <stdio.h>
#include <string.h>
#include <assert.h>
char* my_strcat(char* dest, const char* source)
{
  char* ret = dest;
  assert(dest && source);
  //1.找到目标字符串中的'\0'
  while (*dest)
  {
    dest++;
  }
  //2.将源字符串追加过去
  while (*dest++ = *source++)//赋值
  {
    ;
  }
  return ret;//返回目标空间的起始地址
}
int main()
{
  char arr1[20] = "hello ";
  char arr2[] = "world";
  //my_strcat(arr1, arr2);
  printf("%s\n", my_strcat(arr1, arr2));
  return 0;
}


注意:my_strcat函数不能实现自己追加自己,如果这样做的话,会导致数组越界访问,最后程序崩掉。


#include <stdio.h>
#include <string.h>
#include <assert.h>
char* my_strcat(char* dest, const char* src)
{
  char* cur = dest;
  while (*cur)
  {
    cur++;
  }
  while (*cur++ = *src++)
  {
    ;
  }
  return dest;
}
int main()
{
  char arr[20] = "Joy";
  my_strcat(arr, arr);
  printf("%s\n", arr);
  return 0;
}

9f7c8e1c2561426781fba502dc83030e.png

bc5d951b488f427f86c49e48bb550748.jpg


aa05d3b7c7df479e92bd2f85b28d7fcb.jpg

 所以,使用 my_strcat 和 strcat 函数,一定不能自己追加自己,同时需要注意源字符串要包含 '\0',否则也会出现BUG。还有一个小细节就是,my_strcat 函数和 strcat函数也会将原字符串的 '\0' 拷贝到目标空间中去。


2.3strcmp


intstrcmp (constchar * str1, constchar * str2 );

7536dd97ed8c48e097d93af486cd862f.png


关于strcmp函数,我们需要注意一下几点:


  • 第一个字符串大于第二个字符串,则返回大于0的数字。
  • 第一个字符串等于第二个字符串,则返回0。
  • 第一个字符串小于第二个字符串,则返回小于0的数字。
  • 学会模拟实现strcmp函数。


 代码示例


#include <string.h>
#include <stdio.h>
int main()
{
  char* p = "bbc";
  char* q = "abcdef";
  if (strcmp(p, q) > 0)
    printf(">\n");
  else if (strcmp(p, q) == 0)
    printf("=\n");
  else
    printf("<\n");
  return 0;
}

模拟实现strcmp函数


#include <stdio.h>
#include <assert.h>
int my_strcmp(const char* s1, const char* s2)
{
    assert(s1 && s2);
  while (*s1 == *s2)
  {
        //字符串s1和字符串s2相等
    if (*s1 == '\0')
    {
      return 0;
    }
    s1++;
    s2++;
  }
  if (*s1 > *s2)
  {
    return 1;
  }
  else 
  {
    return -1;
  }
}
int main()
{
  char* p = "abcd";
  char* q = "abc";
  int ret = my_strcmp(p, q);
  if (ret == 0)
  {
    printf("p == q\n");
  }
  else if (ret > 0)
  {
    printf("p>q\n");
  }
  else
    printf("p<q\n");
  return 0;
}


#include <stdio.h>
#include <assert.h>
int my_strcmp(const char* s1, const char* s2)
{
  assert(s1 && s2);
  while (*s1 == *s2)
  {
    if (*s1 == '\0')
    {
      return 0;
    }
    s1++;
    s2++;
  }
  return *s1 - *s2;
}
int main()
{
  char* p = "abcd";
  char* q = "abc";
  int ret = my_strcmp(p, q);
  if (ret == 0)
  {
    printf("p == q\n");
  }
  else if (ret > 0)
  {
    printf("p>q\n");
  }
  else
    printf("p<q\n");
  return 0;
}


 注意:使用 my_strcmp 函数 和 strcmp函数时,两个字符串一定要包含 '\0',否则将会造成数组越界,出现意想不到的结果。


#include <stdio.h>
#include <assert.h>
#include <string.h>
int my_strcmp(const char* s1, const char* s2)
{
  assert(s1 && s2);
  while (*s1 == *s2)
  {
    if (*s1 == '\0')
    {
      return 0;
    }
    s1++;
    s2++;
  }
  return *s1 - *s2;
}
int main()
{
  char arr1[] = { 'a','b','c' };
  char arr2[] = { 'a','b','c' };
  int ret = my_strcmp(arr1, arr2);
  if (ret == 0)
  {
    printf("arr1 == arr2\n");
  }
  else if (ret > 0)
  {
    printf("arr1>arr2\n");
  }
  else
    printf("arr1<arr2\n");
  return 0;
}


以上就是这篇博客的全部内容了,如果大家觉得有收获的话,可以点个赞支持一下哦!


相关文章
|
10天前
|
弹性计算 关系型数据库 微服务
基于 Docker 与 Kubernetes(K3s)的微服务:阿里云生产环境扩容实践
在微服务架构中,如何实现“稳定扩容”与“成本可控”是企业面临的核心挑战。本文结合 Python FastAPI 微服务实战,详解如何基于阿里云基础设施,利用 Docker 封装服务、K3s 实现容器编排,构建生产级微服务架构。内容涵盖容器构建、集群部署、自动扩缩容、可观测性等关键环节,适配阿里云资源特性与服务生态,助力企业打造低成本、高可靠、易扩展的微服务解决方案。
1210 5
|
9天前
|
机器学习/深度学习 人工智能 前端开发
通义DeepResearch全面开源!同步分享可落地的高阶Agent构建方法论
通义研究团队开源发布通义 DeepResearch —— 首个在性能上可与 OpenAI DeepResearch 相媲美、并在多项权威基准测试中取得领先表现的全开源 Web Agent。
1172 87
|
9天前
|
云栖大会
阿里云云栖大会2025年9月24日开启,免费申请大会门票,速度领取~
2025云栖大会将于9月24-26日举行,官网免费预约畅享票,审核后短信通知,持证件入场
1765 12
|
19天前
|
人工智能 运维 安全
|
2天前
|
资源调度
除了nrm-pm,还有哪些工具可以管理多个包管理器的源?
除了nrm-pm,还有哪些工具可以管理多个包管理器的源?
229 127
|
10天前
|
弹性计算 Kubernetes jenkins
如何在 ECS/EKS 集群中有效使用 Jenkins
本文探讨了如何将 Jenkins 与 AWS ECS 和 EKS 集群集成,以构建高效、灵活且具备自动扩缩容能力的 CI/CD 流水线,提升软件交付效率并优化资源成本。
362 0

热门文章

最新文章