strlen、strcpy、strcat、strcmp函数的使用说明和模拟实现

C语言中对于字符和字符串的处理很是频繁,所以出现了许多的作用于字符和字符串的函数,今天,我就对这些函数的其中几个的使用进行说明,并且对每个函数进行模拟实现。



strlen函数

请添加图片描述
由msdn查询可以得知,strlen函数的程序设计为:size_t strlen(const char* str),返回类型为:size_t(无符号整形),参数为const char*,我们接着向下查找,观察strlen函数的返回值和作用。

请添加图片描述
观察返回类型可知,strlen函数返回字符串中的字符个数,不包含’\0’,没有保留返回值为错误。在这里,我还要提出几个strlen函数的需要特别注意的点:
1.字符串中把’\0’作为结束标志,strlen函数返回的是在字符串中’\0’前面出现的字符个数(不包含’\0’)。
2.参数指向的字符中必须要以’\0’结束,如果字符串中没有以’\0’作为结束标志的话,strlen函数就会越界访问,直到找到’\0’,这时就无法计算字符串中的字符个数了,返回的是一个随机值。
3.注意函数的返回值为size_t,是无符号整形。

strlen函数的例子:

#include<stdio.h>
#include<string.h>
int main()
{
	char arr[] = "abcdef";
	int len = strlen(arr);
	printf("%d\n",len);
	return 0;
}

请添加图片描述
由运行结果可以得知,strlen函数计算出了字符数组arr的字符个数,并且没有包含‘\0’。

接下来,我来模拟实现strlen函数。

#include<stdio.h>
#include<string.h>
#include<assert.h>
size_t my_strlen(const char* str)
{
	int len = 0;
	//检验str不是非空指针
    assert(str != NULL); 
	//str不是'\0',len就自增1
	while(*str != '\0')
	{
		len++;
	    str++;
	}
	return len;
}
int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d\n",len);
	return 0;
}

请添加图片描述
由运行结果可知,该strlen函数的模拟实现代码可以满足strlen函数功能要求。另外,在模拟函数的过程中,应当保证返回类型和参数与函数底层的实现相同,这才是模拟实现。

接下来,我将对strlen函数的返回类型为无符号整形作进一步的解释:

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

大家肯定认为该代码实现后会打印 < 吧,因为arr2的长度是小于arr1的长度的,所以strlen函数的返回值是arr1大于arr2,arr2的strlen函数返回值减去arr1的strlen函数返回值肯定是小于0,那第一个if判断语句不成立,自然就运行第二条语句,打印 < ,让我们看看运行结果。
请添加图片描述
运行结果就是这样的出人意外,运行结果为 > 表明if判断语句为真,那么前面我们的分析是哪一步出错了呢,其实我们前面的分析没有错误,只是分析少了一步,我们忽略了strlen函数的返回类型是size_t,arr2的strlen函数返回值是无符号整形,arr1的strlen函数返回值也是无符号整形,无符号整形减去无符号整形依然是无符号整形,在无符号整形下所有的数字都是大于0的,所以两者相减大于0,if判断语句是成立的。



strcpy函数

请添加图片描述
strcpy函数的程序设计为:char* strcpy(char* destination, const char* source),返回类型为char*,两个参数分别是char和const char,接下来,我们来了解一下strcpy函数的返回值:
请添加图片描述
strcpy函数返回destination字符串的地址,没有保存返回值为错误。
接下来,我提出几个关于strcpy函数的注意的点:
1.将source的字符数组拷贝进destination的字符数组,包括’\0’。
2.源字符串也就是source字符串,必须以’\0’结束。(因为strcpy将’\0’拷贝过去才会停止)
3.目标空间也就是destination的字符串的空间要足够大,确保能够存放source的字符串。

strcpy函数的例子:

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[20] = {0};
	char arr2[] = "abcdef";
    printf("%s\n",strcpy(arr1,arr2));
    return 0;
}

请添加图片描述
由前面对strcpy函数的介绍可以得知,strcpy函数返回目标字符的地址,在这里就是arr1的地址,所以打印的就是arr1的内容,而arr1为由原来的全’0’字符数组,变成了有abcdef的字符数组,说明arr2的内容拷贝到了arr1里面。

接下来,我来验证strcpy函数连’\0’也拷贝过去。

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[20] = "xxxxxxxxxxxxxxxxxxx";
	char arr2[] = "abcdef";
    printf("%s\n",strcpy(arr1,arr2));
    return 0;
}

请添加图片描述
调试可以发现arr1中存放着19个x字符和一个’\0’,当我们运行完strcpy函数后,arr1会存放什么呢?
请添加图片描述
由图片可以知道,arr1存放着abcdef’\0’和几个x字符,由该程序就可以证明strcpy函数连源字符串的’\0’也拷贝到目标字符串中。

在使用strcpy函数的过程中,我们必须记住几个要点。
1.源字符串中必须要以’\0’结束,不然strcpy函数在拷贝的过程中,会往源字符串后面越界访问,并且拷贝到目标字符串,直到找到’\0’。
2.目标字符串的空间必须要足够放下源字符串。
3.目标字符串不能是常量字符串,因为常量字符串不能修改,如果有内容拷贝到常量字符串,会导致程序报错。

接下来,我来模拟实现strcpy函数。

#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest,const char* src)
{
	char* ret = dest;
    assert(dest != NULL);
	assert(src != NULL);
	while(*dest++ = *src++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[20] = "abc";
	char arr2[] = "hello bit";
    printf("%s\n",my_strcpy(arr1,arr2));
	return 0;
}

arr1被覆盖前:
请添加图片描述
arr1被覆盖后:
请添加图片描述
运行后结果:
请添加图片描述
由此可见,这个strcpy函数的模拟实现符合了我们的要求。



strcat函数

strcat函数是字符追加函数,它的返回值和参数类型的设置是:
char* strcat (char* destination,const char* source);
在使用strcat函数的过程中,有几个需要注意的点
1.源字符串和目标字符串必须以’\0’结束。
2.目标字符串必须足够大,能容纳下源字符串的内容。
3.目标字符串必须可修改。

strcat函数使用的例子:

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

运行结果如下:
请添加图片描述

由该程序可以得知,strcat函数是将源字符串的内容拷贝到目标字符串内容的后面,那么,源字符串的’\0’是否会被覆盖呢?我利用个小程序来解释一下:

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[20] = "hello \0xxxxxxxxx";
	char arr2[] = "world";
	strcat(arr1,arr2);
	printf("%s\n",arr1);
    return 0;
}

在这里的小程序中,我将arr1后面加上了’\0’和几个’x’,通过调试来发现strcat函数拷贝的过程中,最开始覆盖源字符串函数的哪个位置。

这是arr1最开始的内容:
请添加图片描述
我们需要格外的注意arr1的’\0’的变化。

这是拷贝后的arr1的内容:
请添加图片描述
我们注意到strcat函数拷贝到arr1中,是从下标为6的元素开始覆盖的,那么在未拷贝前,arr1中下标为6的元素是什么呢?我们往文章上面翻找,可以发现arr1在未拷贝前,下标为6的元素是’\0’。(要注意的是,在strcat函数未拷贝前,监视窗口中arr1的下标为6的元素是0,我们需要知道’\0’的阿斯玛值是0,也就是监视窗口显示0,那么该元素就是’\0’)

在这里,我们就可以解释为什么目标字符串中也要有’\0’。
strcat函数是追加字符串的函数,它是通过找到目标字符串的’\0’,来判断该位置是目标字符串的末尾,再从’\0’开始覆盖目标字符串。

源字符串中要有’\0’是为了找到源字符串的末位置,方便拷贝停止,与strcpy函数的原因相同。

接下来,我来模拟实现strcat函数:

#include<assert.h>
#include<stdio.h>
char* my_strcat(char* dest,const char* src)
{
    char* ret = dest;
	assert(dest != NULL);
	assert(src != NULL);
	//在源字符串中找到'\0'的位置
	while(*dest != '\0')
	{
	    dest++;
	}
	//从'\0'开始拷贝
	while(*dest++ = *src++)
	{
	    ;
	}
	return ret;
}
int main()
{
	char arr1[20] = "hello ";
	char arr2[] = "world";
	my_strcat(arr1,arr2);
	printf("%s\n",arr1);
    return 0;
}

运行结果如下:
请添加图片描述
这个strcat函数的模拟实现达到了我们的要求。

接下来,我提出一个问题,字符串给自己追加后如何?如下:

#include<stdio.h>
#include<string.h>
int main()
{
    char arr1[20] = "abcdef";
	strcat(arr1,arr1);
	return 0;
}

在VS调试的过程中,程序报出了错误。
请添加图片描述

接下来,我来解释为什么会报错。

这是使用strcat函数最开始传参时的样子,dest和src都指向字符数组的首元素。
请添加图片描述

接下来,dest开始往后面走,直到找到’\0’,最后就是下面的结果。
请添加图片描述

然后,dest开始被src覆盖,并且覆盖一次,src和dest就往后走一步。(为了方便观看,我将覆盖后的元素写在原来的元素下面)

请添加图片描述
'\0’被a所覆盖,接下来,src和dest各自往后面走一步。
请添加图片描述

按这样的过程持续下去,会出现这样的结果。请添加图片描述
在原来的strcat函数的设置中,如果将’\0’拷贝到过去以后,strcat函数就会停止拷贝,换句话说,如果strcat函数没有拷贝’\0’过去,那么该函数就永远不会停止拷贝。

由图片可以得知,src已经到达了’\0’的位置,那么strcat函数将src中的’\0’拷贝到dest就可以停止运行了,但遗憾的是’\0’早已被’a’字符所替代,所以拷贝过去的是’a’字符,就这样,程序一直找不到’\0’,进入死循环的拷贝,最终,程序报错。



strcmp函数

strcmp函数的返回值和参数类型的设置是:
int strcmp(const char* str1,const char* str2)
请添加图片描述

该strcmp函数的返回值有标准规定:
请添加图片描述
标准规定:
1.第一个字符串小于第二个字符串,返回小于0的数字。
2.第一个字符串等于第二个字符串,返回等于0的数字。
3.第一个字符串大于第二个字符串,返回大于0的数字。

在msdn查询之后,我们对strcmp函数的了解就进一步加深了,我再提出一个问题,该函数是怎么判断两个字符串的大小关系的呢?
别急,接下来我来给大家讲解。
请添加图片描述
在使用strcmp函数的过程中,会将字符串的首个字符串的地址传过去,如上图,strcmp函数就会解引用str1和str2,比较两者的阿斯玛值的大小关系,如果两个阿斯玛值相等,str1和str2就会往后走一步,为下次比较做准备。
那么就会有以下五种情况。
1.str1所在地址的的字符的阿斯玛值与str2所在地址的字符的阿斯玛值相等,那么str1和str2就会向后走一步。
2.str1所在地址的字符的阿斯玛值比str2所在地址的字符的阿斯玛值大,直接返回大于0的数字,strcmp函数的调用结束。
3.str1所在地址的字符的阿斯玛值比str2所在地址的字符的阿斯玛值小,直接返回小于0的数字,strcmp函数的调用结束。
4.str1和str2两个同时到达’\0’的地址,两个字符串都比较完了也没有出现字符不相等的情况,那么返回等于0的数字,strcmp函数的调用结束。
5.str1和str2的其中一个先到达’\0’,这里要注意的是,'\0’的阿斯玛值是0,所以依然可以拿来比较,返回值同上面2,3规则。

接下来,我举个strcmp函数的使用例子。

#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcde";
	int ret = strcmp(arr1, arr2);       //arr1大于arr2,返回大于0的数字
	printf("%d\n", ret);

	char arr3[] = "abcdef";
	char arr4[] = "abcdefg";
	ret = strcmp(arr3, arr4);          //arr3小于arr4,返回小于0的数字
	printf("%d\n", ret);

	char arr5[] = "abcdef";
	char arr6[] = "abcdef";
	ret = strcmp(arr5, arr6);         //arr4等于arr5,返回等于0的数字
	printf("%d\n", ret);

	return 0;
}

请添加图片描述
接下来,我来模拟实现strcmp函数。

#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 != NULL);
	assert(str2 != NULL);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
		{
			return 0;
		}
		str1++;
		str2++;
	}
	if (*str1 > *str2)
	{
		return 1;
	}
	else
	{
		return -1;
	}
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcde";
	int ret = my_strcmp(arr1, arr2);       //arr1大于arr2,返回大于0的数字
	printf("%d\n", ret);

	char arr3[] = "abcdef";
	char arr4[] = "abcdefg";
	ret = my_strcmp(arr3, arr4);          //arr3小于arr4,返回小于0的数字
	printf("%d\n", ret);

	char arr5[] = "abcdef";
	char arr6[] = "abcdef";
	ret = my_strcmp(arr5, arr6);         //arr4大于arr5,返回大于0的数字
	printf("%d\n", ret);

	return 0;
}

请添加图片描述

在C语言中,strcpy、strcat、strcmp函数统称为长度不受限制的字符串函数,在下一篇的文章中,我会介绍几个长度受限制的字符串函数,关注点一点,下期更精彩。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值