C语言函数:字符串函数及模拟实现strlen() 、strcpy()、 strcat()

C语言函数:字符串函数及模拟实现strlen() 、strcpy()、 strcat()

提示:字符串后有默认会有\0,而数组不会。

strlen()函数:

        作用:获取字符串的长度。

 
 

模拟实现strlen:

#define _CRT_9SECURE_NO_WARNINGS

#include <stdio.h>
#include<stdio.h>

int my_strlen(const char* str)//从始至终str不改变,加上const使代码更完整
{
	int count = 0;//计数器
	assert(str != NULL);//防止空指针,如果是是空指针就会报错,并停止程序。提高了程序严谨性
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}

int main()
{
	char arr[] = "abcde";
	int ret = my_strlen(arr);//模拟实现的strlen
	printf("%d\n", ret);
	return 0;
}

模拟实现strlen()有三种方法:

①计数器(上面使用的)

②递归 

③指针-指针(等于指针之间的个数)

我们模拟的strlen函数是int类型(有符号整形),原本的strlen函数size_t类型(无符号整形),看以下代码,输出的结果会是:>

因为无符号整数和无符号整数相减,得到的也是无符号整数,正常相减得到-3,但是无符号整数从内存中取出来时,将会是一个非常大的正数(>0),所以输出:>

此时使用my_strlen函数,输出的结果是:<=

因为int是有符号整数,有符号整数减有符号整数,得到的也是有符号整数,得到的就是-3(<0),所以输出的是:<=

所以,如果要完全实现strlen函数,就需要把返回值改成无符号整形

strcpy()函数:

        作用:将某地址指向的数据拷贝到另一个地址指向的地方

       strcpy()函数参数都是地址:

        第一个destination是目标地址,就是把某数据拷贝到第一个参数地址中

        第二个source是原数据地址,就是想要拷贝的数据。

拷贝的方式:

        从source拿到数据,放到destination。一个source地址对应一个destination地址。

        意思就是,如果destination地址所对应的数组或者字符串内数据。会被source地址对应的数据给覆盖

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include<assert.h>
#include <stdlib.h>

int main()
{
	char arr[20] = "##########";//10个#
	strcpy(arr,"hello");
	printf("%s\n", arr);
	//结果:hello
	return 0;
}

        因为"hello"实际上是"hello\0",放到arr中会把\0也放入。printf使用%s打印时,%s遇到\0结束。因此打印了:hello

source地址是字符串:

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include<assert.h>
#include <stdlib.h>

int main()
{
	char arr[20] = {0};
	strcpy(arr,"hello world");
	printf("%s\n", arr);
	//结果:hello world

	return 0;
}

        strcpy中的传参"hello world"是指向字符 'h' 的地址

source地址是数组:

int main()
{
	char arr[] = "##########";//10个#
	char arr2[] = { 'a','b','c' };
	strcpy(arr,arr2);
	printf("%s\n", arr);
	//结果:abcxxxxxxxxxx(x表示乱码)
	//结果也有可能是:程序卡死 或 是abc
	return 0;
}

三种结果的解释:

        从arr2首元素地址开始,找到\0结束,因为数组中不会默认存放\0,因此strcpy会在内存中从arr2首元素地址找,找到\0结束。 

        1.abc乱码:strcpy在找\0中,如果在内存中超出了arr2(越界访问)并且找到了\0,那么两边之间的内容就会很可能变成乱码,因此打印abcxxxxxxxx(x表示乱码)。

        2.abc:strcpy在找\0中,如果在内存中arr2后刚好是\0就会,那么就会打印abc。(尝试过,循环这个程序,跑了10分钟也没有出现abc这个结果,可想而知这个可能极其小。)

        3.程序卡死:strcpy在找\0中,如果在内存中超出了arr2(越界访问)并且没找到一直\0,越界访问的范围太大,那么就会使程序卡死。

数组空间问题:

int main()
{
	char arr[5] = "####";
	char* p = "hello world";
	strcpy(arr, p);
	printf("%s\n", arr);
	return 0;
}

程序结果是:hello world

但是,会报错,这个错误是关于“arr空间不足,放不下hello world”的一个错误。

能够正常输出hello world是的原因是:因为传入了\0,并且%s打印遇到\0结束。

变量与常量:

int main()
{
	char* arr = "###############";
	char* p = "hello world";
	strcpy(arr, p);
	printf("%s\n", arr);
	return 0;
}

char* arr是常量字符串,无法被修改。如果使用strcpy修改arr就会报错。因此我们需要知道:

destination地址所指向的内容必须是能被修改的。source地址所指向的内容均可。

strcpy()函数模拟实现:

        两种写法:        

        第一种(常规写法):

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include<assert.h>
#include <stdlib.h>

char* my_strcpy(char* de, const char* so)
{
    assert(de && so);//防止是NULL
    //NULL的ASCLL是0,所以de && so可以保证两个都不为NULL
	int i = 0;
	while(*(so+i)!='\0')//控制在\0之前的全部改成so
	{
		*(de + i) = *(so + i);
		i++;
	}
	*(de + i) = *(so + i);//把\0也换过去
	return de;
}

int main()
{
	char arr[] = "###############";
	char* p = "hello world";
	my_strcpy(arr, p);
	printf("%s\n", arr);
	return 0;
}

        第二种:

        

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include<assert.h>
#include <stdlib.h>

char* my_strcpy(char* de, const char* so)
{
	assert(de && so);
	char* dr = de;//因为下面是++,直接改变了dr,所以需要有一个变量保存初始化的值,这样才能返回de
	while (*de++ = *so++)//后置++,都从0开始。=是赋值操作符,因此so可以把每个值都给de,包括\0
	{//遇到\0时就会停止,因为\0的ASCLL码值是0
		;
	}
	return de;
}

int main()
{
	char arr[] = "###############";
	char* p = "hello world";
	my_strcpy(arr, p);
	printf("%s\n", arr);
	return 0;
}

 strcat()函数:

        作用:追加字符或字符串到destination后

 strcat()函数参数都是地址:

        第一个destination是目标地址,就是把某数据追加到第一个参数地址中

        第二个source是原数据地址,就是想要用来追加的数据。

strcat()函数基本使用:

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include<assert.h>
#include <stdlib.h>
int main()
{
	char arr[20] = "hello ";
	char arr2[] = "world";
	strcat(arr, arr2);
	printf("%s\n", arr);
	//结果是:hello world
	return 0;
}

strcat()函数追加的工作原理:

        还是上面的代码,不同你的是arr变成了char arr[20] = "hello \0#############";

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include<assert.h>
#include <stdlib.h>

int main()
{
	char arr[20] = "hello \0#############";
	char arr2[] = "world";
	strcat(arr, arr2);
	printf("%s\n", arr);
	//结果是:hello world
	return 0;
}

        

         总结:strcat函数会使用第二个参数从第一个参数的'\0'开始,不断覆盖。最后会把第二个参数的'\0'也给传过去。

strcat()函数的模拟实现:

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include<assert.h>
#include <stdlib.h>

char* my_strcat(char* de ,char* so)
{
	assert(de && so);//保证两个参数不为空指针
	char* ret = de;//保留de起始值
	while (*de)//找到\0的位置;
	{
		de++;
	}
	while (*de++ = *so++)//上面的strlen()有说;
	{
		;
	}
	return ret;//返回de起始值
}

int main()
{
	char arr[20] = "hello \0#############";
	char arr2[] = "world";
	printf("%s\n", my_strcat(arr, arr2));
	//结果是:hello world
	return 0;
}

        原理: 先找到de字符串的\0,然后从de的\0开始往后覆盖so的字符串,把so的\0放入de后,返回起始de的值(strcat函数要求返回de的起始值)

        strcat是不能自己给自己追加的:

        

 
#define _CRT_SECURE_NO_WARNINGS
 
#include <stdio.h>
#include<assert.h>
#include <stdlib.h>
 
int main()
{
    char arr[20] = "abc";
    strcat(arr, arr);
    printf("%s\n", arr);
    return 0;
}

       自己给自己追加,abc/0的/0会被替换成a,再追加bc,但是地址相同当要拿/0的时候,/0已经被改成a了,最后会abcabcabc...永远找不到\0

        

        但是不同的的编译器或者不同的版本可能可以这样,比如vs2022版本

        

        可以追加的原因是因为,在strcat内部多了一个局部指针。

        

char * likestrcat(char *dst,const char *src)
 {
    char * srccopy=(char*)malloc(sizeof(*src)*sizeof(src));
 
    std::memcpy(srccopy,src,sizeof(*src)*sizeof(src));
 
      char *cp = dst;
 
      while ( *cp )
         cp++;
 
      while ( *cp++ = *srccopy++ )
          ;
 
      free(srccopy);
     return ( dst );
}
 

        相当于多存了一份src

        如此,自己给自己追加时,des和src中src会拷贝一份自己,这样des和src就不会拥有相同的指针了,这样就可以自己给自己追加

        

        也可以通过strncat来自己给自己追加,因为strncat是自己放\0:

        C语言函数:字符串函数及模拟实现strncpy()、strncat()、strncmp()_srhqwe的博客-CSDN博客

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

srhqwe

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

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

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

打赏作者

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

抵扣说明:

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

余额充值