(一)字符函数和字符串函数详细讲解和模拟实现(优化)

简介: (一)字符函数和字符串函数详细讲解和模拟实现(优化)

❤️字符串函数讲解及模拟实现

📕长度不受限制的字符串函数:

1.strcpy函数

库函数strcpy

char * strcpy ( char * destination, const char * source );
  • 将源指向的C字符串复制到目标指向的数组中,包括终止的空字符(并在此时停止)。

注意:


  • 源字符串必须以 ‘\0’ 结束。(尤其注意arr[ ]={‘a’,‘b’,‘c’};这种’ \0 '不确定的;)
  • 会将源字符串中的 ‘\0’ 拷贝到目标空间。(会以source首元素会替代destination末尾’\0’)
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可变。

目标空间必须可修改(强调)

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

这个代码一运行就会崩掉,原因就是目标空间为字符串,无法被修改,所以无法拷贝;

模拟实现:

#include <stdio.h>

void my_strcpy(char* dest,char* src);

int main()
{
   char arr1[20]="asdfghjkl";
   char arr2[]="hello!";
   
   my_strcpy(arr1,arr2);
   //把arr2拷贝到arr1中;
   printf("%s",arr1);
   
   return 0;
}
  • 模拟实现1:
char* my_strcpy(char* dest,char* src)
{
   while(src!='\0')
   char* ret=dest;
   {
     *dest=*src;
      dest++;
      src++;
  }
  //成功拷贝hello!
  *dest=*src;//拷贝'\0'
  return ret;
}
  • 优化:
#include<assert.h>
char* my_strcpy(char* dest,const char* src)
//我们是要src拷贝到dest,const让无需更改的来源地址变为常变量,无法修改。可防止后面*dest=*src写反;
{
   assert(src!=NULL)//断言
   assert(dest!=NULL)//断言
   //提高可调式性
   char* ret=dest;
   while(*dest++=*src++)
   {
          ;
    //直接打印hello!\0
   }
   return ret;
}


2.strcat函数

库函数strcat

char * strcat ( char * destination, const char * source );
  • 将源字符串的副本附加到目标字符串中。目标中的终止空字符将被源的第一个字符覆盖,并且在目标中两者的连接形成的新字符串的末尾包含一个空字符。

注意:

  • 源字符串必须以 ‘\0’ 结束。
  • 目标空间必须有足够的大,能容纳下源字符串的内容。
  • 目标空间必须可修改。

模拟实现:

char* my_strcat(char* dest, const char* src)
{
  assert(dest && src);
  char* ret = dest;
  //查找\0;
  while (*dest)
    dest++;
  //追加;
  while (*src)
  {
    *dest++ = *src++;
  }
  return ret;
}

3.strcmp函数

库函数strcmp

int strcmp ( const char * str1, const char * str2 );
  • 这个函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续使用以下的对,直到字符不同或达到终止的空字符。

注意:


  • 标准规定: 第一个字符串大于第二个字符串,则返回大于0的数字 第一个字符串等于第二个字符串,则返回0 第一个字符串小于第二个字符串,则返回小于0的数字
  • 库函数中一般相等返回0;大于返回1;小于返回-1。
#include<stdio.h>
#include<string.h>
int main()
{
  char* p1 = "abcdef";
  char* p2 = "abcdef";
  char* p3 = "abcd";
  char* p4 = "bcde";
  printf("%d\n", strcmp(p1,p2 ));
  printf("%d\n", strcmp(p1,p3 ));
  printf("%d\n", strcmp(p3,p4 ));
}

  • 这里稍微补充下:我们不能进行*arr>*arr1或者“asdsfa”>“asw”比较来比较字符串大小,要借助函数。
  • 模拟实现:
int my_strcmp(const char* p1,const char* p2)
{
  assert(p1 && p2);
  while (*p1 == *p2)
  {
    if (p1 == '\0')
    //这里是表达当他们同时为'\0'就表明相等
    {
      return 0;
    }
    //如果都相等且都不等于'\0';进行++比较下一位;
    p1++;
    p2++;
  }
  //比较不相同的字符,返回差值(这里有整形提升)
  return *p1 - *p2;
}
//再借助判断即可;


📙长度受限制的字符串函数:

1.strncpy函数

库函数strncpy

char * strncpy ( char * destination, const char * source, size_t num );
  • 将源文件的第一个数个字符复制到目标文件。如果在复制num字符之前找到源C字符串的结尾(由空字符表示的信号),则将用零填充目标,直到写入了全部num字符为止。

注意:

  • 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加\0,直到num个

VS2019实现:

char * __cdecl strncpy (char * dest,const char * src,size_t count)
{
        assert(dest&&src);
        char *start = dest;

        while (count && (*dest++ = *src++) != '\0')    //拷贝到dest中,并且用count限制
                count--;

        if (count)                             
                while (--count)
                        *dest++ = '\0';               //完成第二步:长度不够,补'\0';

        return(start);
}

2.strncat函数

库函数strncat

char * strncat ( char * destination, const char * source, size_t num );
  • 将源代码的第一个num字符附加到目标代码中,再加上一个终止的空字符。如果源代码中的C字符串的长度小于num,则只复制直到终止的空字符的内容。
  • VS2019实现:
char * __cdecl strncat(char * front,const char * back,size_t count)
{
        char *start = front;

        while (*front++)
                ;
        front--;

        while (count--)
                if ((*front++ = *back++) == 0)
                        return(start);

        *front = '\0';
        return(start);
}

3.strncmp函数

库函数strncmp

int strncmp ( const char * str1, const char * str2, size_t num );
  • 这个函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续使用以下的对,直到num个数。

注意:

  • 比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。
  • VS2019实现:
int __cdecl strncmp(const char *first,const char *last,size_t  count)
{
    size_t x = 0;

    if (!count)
    {
        return 0;
    }

    /*
     * This explicit guard needed to deal correctly with boundary
     * cases: strings shorter than 4 bytes and strings longer than
     * UINT_MAX-4 bytes .
     */
    if( count >= 4 )
    {
        /* unroll by four */
        for (; x < count-4; x+=4)
        {
            first+=4;
            last +=4;

            if (*(first-4) == 0 || *(first-4) != *(last-4))
            {
                return(*(unsigned char *)(first-4) - *(unsigned char *)(last-4));
            }

            if (*(first-3) == 0 || *(first-3) != *(last-3))
            {
                return(*(unsigned char *)(first-3) - *(unsigned char *)(last-3));
            }

            if (*(first-2) == 0 || *(first-2) != *(last-2))
            {
                return(*(unsigned char *)(first-2) - *(unsigned char *)(last-2));
            }

            if (*(first-1) == 0 || *(first-1) != *(last-1))
            {
                return(*(unsigned char *)(first-1) - *(unsigned char *)(last-1));
            }
        }
    }

    /* residual loop */
    for (; x < count; x++)
    {
        if (*first == 0 || *first != *last)
        {
            return(*(unsigned char *)first - *(unsigned char *)last);
        }
        first+=1;
        last+=1;
    }

    return 0;
}


📘求字符串长度:

1.strlen函数:

库函数strlen

size_t strlen ( const char * str );
  • 读取字符串长度,并返回长度。
  • 注意:
  • 字符串已经 ‘\0’ 作为结束标志,strlen函数返回的是在字符串中 ‘\0’ 前面出现的字符个数(不包
    含 ‘\0’ )。
  • 参数指向的字符串必须要以 ‘\0’ 结束。
  • 注意函数的返回值为size_t,是无符号的( 易错 )因为统计字符串长度都为正数。

易错点补充:

思考:输出的结果是大于还是小于?

#include<stdio.h>
#include<string.h>
int main()
{
  char arr[] = "asdasf";
  char arr1[] = "uqiwhdqio";
  if (strlen(arr) - strlen(arr1) < 0)
  {
    printf("小于");
  }
  else
  {
    printf("大于");
  }
  return 0;
}

  • 为什么结果是大于?难道不是我们理解的6-9=-3吗?别急听我为你解释。


  • 在库函数<string.h>中strlen返回的是size_t(也就是我们理解的无符号类型)
  • 我们说(有符号数-有符号数)得到有符号数,同样的(无符号数-无符号数)得到的也是无符号数(如果计算机没有别的格式化要求);格式化要求比如printf,用%d来打印这就是一种格式化要求;
#include<stdio.h>
#include<string.h>
int main()
{
  char arr[] = "asdasf";
  char arr1[] = "uqiwhdqio";
  printf("%u", strlen(arr) - strlen(arr1));
  //为了展现我们就以%u打印(计算机中也默认为无符号数,上述打印大于就能证明);结果为4294967293
  return 0;
}```
==>具体的计算过程:<==
//6----->00000000 00000000 00000000 00000110 原码--->补码
//9----->00000000 00000000 00000000 00001001 原码--->补码
//-9---->10000000 00000000 00000000 00001001 原码
//-9---->11111111111111111111111111111110111 补码
//-3---->11111111 11111111 11111111 11111101 无符号数打印

模拟实现:

  • 优化方式和strcpy相近,若没看上述,建议先看更好,观感更佳,这就直接代码体现:
  • 主函数:
int main()
{
 char arr[]='ashduhewu';
 
 printf("%d",my_strlen(arr));

  return 0;
}
  • 1.常规实现:
#include<stdio.h>
#include<assert.h>

size_t my_strlen(const char* str)
//size_t相当于unsigned int,因为字符串长度一定为正整数,并且传入的字符串无需更改,所以加上const修饰*str;
{
    assert(str);
    //直接写str,若str为NULL,返回的是0,判断为假,报错提醒;
    int count = 0;
    while(str!='\0')
    {
       count++;
       str++;
  }
   return (unsigned int) count;
}
  • 2.递归实现:
int my_strlen(const char* str)
{
  if (*str == '\0')
    return 0;
  else return 1 + my_strlen(str + 1);
  //先遍历,后返回,总返回:return 【(*str=='\0';遍历结束,开始返回)0+1+1+1+···+1】
}

  • 3.指针实现:
int my_strlen(const char* str)
{
  char* p = str;
  while (*p != '\0')
    p++;
  //得到末尾地址
    return (p - str);
  //返回得到两地址间数组元素个数的绝对值
  //数组元素是低地址到高地址排列,所以建议用末尾地址减去初始地址,得到正数;
}

Tips:细心的小伙伴已经发现了,下面两个函数模拟,我们返回类型是int而并非size_t(unsigned int),这里是想要告诉大家,库函数只是一个给定的方式,仅仅是一种方式(当然也是比较好的方式),但我们要根据所需进行自己的更改!

相关文章
|
Java 程序员
收藏!阿里毕玄16篇文章,深度讲解Java开发、系统设计、职业发展
阿里毕玄结合自己的经历深度讲解Java开发、系统设计、职业发展等问题,快来一键收藏吧。
35177 1
Windows下的CMake下载与安装
Windows下的CMake下载与安装
Windows下的CMake下载与安装
|
存储 Java C#
30 如何在Swift中实现继承
如何在Swift中实现继承
140 0
|
Web App开发 安全 中间件
谷歌、火狐、Edge等浏览器如何使用ActiveX控件
allWebPlugin 是一款为用户提供安全、可靠且便捷的浏览器插件服务的中间件产品,支持 Chrome、Firefox、Edge 和 360 等浏览器。其 V2.0.0.20 版本支持一个页面加载多个插件,并解决了插件与浏览器之间的焦点问题。用户可通过“信息化系统 + allWebPlugin + 插件 + 浏览器”的解决方案实现 ActiveX 插件的无缝集成。下载地址见文末,安装包含详细说明。
3434 119
golang对遍历目录操作的优化
【8月更文挑战第7天】在Golang中优化目录遍历能提升性能。可通过缓冲读取减少系统调用、使用协程并发处理大量文件、按需跳过不必要目录及仅获取所需文件信息等方式实现。示例代码展示了如何运用协程并行遍历子目录以加快处理速度。实际应用时需依据场景选择合适策略。
216 1
|
SQL 关系型数据库 数据库
Schema(模式
【10月更文挑战第11天】
1153 8
|
图形学
【制作100个unity游戏之25】3D背包、库存、制作、快捷栏、存储系统、砍伐树木获取资源、随机战利品宝箱10(附带项目源码)
【制作100个unity游戏之25】3D背包、库存、制作、快捷栏、存储系统、砍伐树木获取资源、随机战利品宝箱10(附带项目源码)
269 1
|
存储 弹性计算 文件存储
对象存储OSS产品常见问题之OSS Bucket 创建好后更改存储类型如何解决
对象存储OSS是基于互联网的数据存储服务模式,让用户可以安全、可靠地存储大量非结构化数据,如图片、音频、视频、文档等任意类型文件,并通过简单的基于HTTP/HTTPS协议的RESTful API接口进行访问和管理。本帖梳理了用户在实际使用中可能遇到的各种常见问题,涵盖了基础操作、性能优化、安全设置、费用管理、数据备份与恢复、跨区域同步、API接口调用等多个方面。
480 0
|
SQL 存储 Oracle
Oracle数据库中游标的工作原理与优化方法
Oracle数据库中游标的工作原理与优化方法
|
关系型数据库 MySQL Linux
【mysql】MySql主从复制,从原理到实践!
【mysql】MySql主从复制,从原理到实践!
475 0