C语言<string.h>头文件所包含的部分函数的解析

1.strlen

strlen函数的作用是求字符串的长度,传入字符串的首元素地址,返回该字符串的长度。

注意这里返回值的参数为size_t,即无符号整形,所以返回值ret也应该是size_t类型。而且由于对无符号整形进行加减运算会得到无符号整型,所以无法通过比较两个函数返回的strlen值进行长短比较。 

//模拟实现strlen函数
size_t my_strlen(const char* arr)
{
	assert(arr);//断言,保证arr不为空指针
	size_t ret = 0;//使用变量进行计数
	while (*arr++)//当*arr=='\0'跳出循环,否则进入循环
		ret++;//长度+1
	return ret;
}

 2.strcpy

strcpy函数的作用是将源字符串的内容拷贝到目标空间。

注意

1.源字符串必须以\0结尾,否则拷贝会继续向后进行,直到遇到\0。

2.目标字符串必须要有足够的空间

//模拟实现strcpy函数
char* my_strcpy(char* dest, const char* src)
{
	assert(dest && src);//断言
	char* ret = dest;//保存目标字符串首元素地址以便返回
	while (*dest++ = *src++)//进行拷贝
		;
	return ret;
}

3.strncpy

 strncpy的作用是将源字符串中指定数量的字符拷贝到目标空间中。

但是也有需要注意的地方:

如果源字符串字符不够,会自动用\0补充。比如源字符串为"abc",转移6个字符,则会拷贝a b c \0 \0 \0到目标空间。

4.strcat

strcat函数的作用是追加一个字符串到目标字符串的结尾。传入目标字符串的首元素地址,与源字符串的首元素地址,返回目标字符串的首元素地址。

使用strcat要注意三点

1.源字符串以必须\0结尾以停止追加

2.目标字符串必须有足够空间,所以目标字符串必须指定大小

3.目标可以被修改

//模拟实现strcat函数
char* my_strcat(char* dest, const char* src)
{
	assert(dest && src);//断言
	char* ret = dest;//保存目标字符串首元素地址以便返回
	while (*dest)//从目标空间起始地址向后寻找\0
		dest++;
	while (*dest++ = *src++)//进行追加
		;
	return ret;
}

可以看到strcat的模拟实现与strcpy的模拟实现过程非常相似,仅仅是增加了寻找目标字符串结尾位置的步骤。

5.strncat

strncat和strcat的作用类似,但是指定了追加字符的数量。

要注意,如果源字符串没有以\0结尾,则会在追加结束后再追加上\0,且最多只追加一个\0。

6.strcmp

strcmp的作用是进行两个字符串的比较,这里比较的不是字符串的长度,而是比较字符的大小。

假设传入的两个数组为strcmp(arr1,arr2)

这里比较大小的规则为,从两个字符串的第一个字符开始,若同一位字符相同,则比较下一位,若不同,当arr1内的元素ASCII码值大于arr2内的元素,那么返回一个大于0的数,当arr2内的元素ASCII码值大于arr1内的元素,则返回一个小于0的数。若直到两个字符串结束两者都没有出现不等的情况,则返回0,表示两个字符串完全相等。

例如 字符串"abcd"与"abc"比较则返回值大于0(因为d的ASCII码值大于abc后的'\0'的ASCII码值)

"abcd"与"abcc"比较返回值大于0(因为d的ASCII码值大于c的ASCII码值)

而"abc"与"abc"比较则返回0

//模拟实现strcmp函数
int my_strcmp(const char* arr1, const char* arr2)
{
	assert(arr1 && arr2);//断言
	while (*arr1 == *arr2 && *arr1)//当arr1内元素与arr2内元素相等且不为'\0'进入循环
		arr1++,arr2++;//指针分别+1,指向下一个元素
	return *arr1 - *arr2;//返回比较值,当两者相等时arr1 arr2都指向'\0' 相减为数字0
}

7.strncmp

将两个字符串的前n个字符以strcmp的规则进行比较

8.strtok

strtok函数将把第一个遇到的分割符改为\0,返回第一个分割好的字符串的首元素地址,传入空指针NULL则会从第一个分割符的位置向后查找下一个分割符,如果没有可检索的字符串,则返回一个空指针。

例子:被检索的字符串arr1="ab#cd@ef" 以及分割符字符串arr2="#@",要将arr1进行分割,则

第一次使用:strtok(arr1,arr2),返回值为元素a的地址 arr1="ab\0cd@ef"

第二次使用:strtok(NULL,arr2),返回值为元素c的地址 arr1="ab\0cd\0ef"

第三次使用:strtok(NULL,arr2),返回值为元素e的地址 arr1="ab\0cd\0ef"

注意在第三次使用时,虽然没有寻找到分割符,但是strtok仍对e开头的字符串进行了检索,所以并没有返回空指针,而是返回了元素e的地址。

9.strstr

strstr函数的作用是返回字符串中首次出现子串的地址,找不到则返回NULL,并且当字串为空字符串时,返回字符串的首元素地址。

模拟实现的逻辑为,首先判断arr2是否为空字符串,再进行查找。

查找过程为:每次进入循环先判断arr1是否指向\0,若指向\0则说明arr1遍历结束也没有找到字串,退出循环,返回NULL。若没有指向\0则进入循环,对以arr1为首元素的字串进行判断,若没有找到,则arr1指向下一个元素。

对以arr1为首元素的字串进行判断过程如下:初始化str1与str2指针,分别赋值为arr1与arr2,以供当元素相等时向后查找,当查找过程中str1不等str2或者任意一方指向元素为\0时退出循环,判断str2所指向元素是否为\0。若为\0,则说明找到了字串,此时返回arr1。

这里设计str1与str2的原因在于在判断时要对每一个字符为首的子字符串进行判断,若只使用arr1与arr2,当在"abbbcd"中查找"bbc"时,在arr1指向第一个b时,"bbb"并不能与"bbc"匹配,但在下一次判断时arr1直接指向了d,arr2指向了子串里的c,导致无法正常进行第二次判断,结果出错。

//模拟实现strstr函数
char* my_strstr(const char* arr1,const char* arr2)
{
	assert(arr1 && arr2);//断言
	if (!*arr2)//如果arr2为空字符串,返回arr1的首元素地址
		return (char*)arr1;
	while (*arr1)//arr1不指向\0时进入循环
	{
		char* str1 = (char*)arr1;//定义两个指针进行字符串的匹配
		char* str2 = (char*)arr2;
		while (*str1 && *str1 == *str2)//当两者不为\0且相等时进入循环
		{
			str1++;//指针分别指向下一个元素
			str2++;
		}
		if (!*str2)//若结束循环后str2指向\0则说明找到字串
			return (char*)arr1;
		arr1++;//匹配失败时arr1指向下一个字符
	}
	return NULL;//找不到则返回NULL
}

10.memcpy

memcpy函数的作用是将源空间的数据以字节为单位拷贝到目标空间。并且返回目标空间的地址。

//模拟实现memcpy函数
void* my_memcpy(void* dest, const void* src, size_t count)
{
	assert(dest && src);//断言
	void* ret = dest;//保留目标空间地址以便返回
	while (count--)//拷贝count个字节的数据
	{
		*(char*)dest = *(char*)src;//拷贝
		dest = (char*)dest + 1;//指针指向下一个字节
		src = (char*)src + 1;
	}
	return ret;
}

11.memmove

memmove和memcpy的区别是当内存放生局部重叠时,memmove保证结果正确,memcpy不保证拷贝结果正确。memmove会检测拷贝的进行方向并且以不会出错的方向进行拷贝。

为什么说memcpy可能发生错误呢?比如将数组{1,2,3,4,5,6,7,8,9,10}内的1 2 3 4拷贝到 3 4 5 6的位置,按逻辑,结果应该是1,2,1,2,3,4,7,8,9,10

但是从左向右拷贝

拷贝一个整形后 第一个整形1拷贝到第三个整形的位置后数组变为{1,2,1,4,5,6,7,8,9,10}

拷贝两个整形后 第二个整形2拷贝到第四个整形的位置后数组变为{1,2,1,2,5,6,7,8,9,10}

拷贝三个整形后 第三个整形1拷贝到第五个整形的位置后数组变为{1,2,1,2,1,6,7,8,9,10}

拷贝四个整形后 第三个整形2拷贝到第六个整形的位置后数组变为{1,2,1,2,1,2,7,8,9,10}

这与我们设想的结果不同,因为我们拷贝的数据将原有位置的数据覆盖,导致无法正常进行拷贝。但是memmove在拷贝前会进行方向判断,以保证拷贝的结果正确。在上面的例子中,从右向左拷贝,则

拷贝一个整形后 数组变为{1,2,3,4,5,4,7,8,9,10}

拷贝两个整形后 数组变为{1,2,3,4,3,4,7,8,9,10}

拷贝三个整形后 数组变为{1,2,3,2,3,4,7,8,9,10}

拷贝四个整形后 数组变为{1,2,1,2,3,4,7,8,9,10}

也就正常完成了我们的拷贝

//模拟实现memmove函数
void* my_memmove(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	if (dest > src)//判断目标空间与源空间的关系
	{//从右向左拷贝
		dest = (char*)dest + count - 1;
		src = (char*)src + count - 1;
		while (count--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest - 1;
			src = (char*)src - 1;
		}
	}
	else
	{//从左向右拷贝
		while (count--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	return ret;
}

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值