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

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

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

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),这里是想要告诉大家,库函数只是一个给定的方式,仅仅是一种方式(当然也是比较好的方式),但我们要根据所需进行自己的更改!


-------------------------✨萌新初上道,大佬多指教!!!----------------------------------------

在这里插入图片描述

  • 22
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 23
    评论
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

胖胖龙大兄很忙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值