冰冰学习笔记:strncpy,strncat,strncmp

上一节我们将到了长度不受限制的字符串处理函数,接下来我们学习长度受限的字符串长度。

这些函数接收一个显式的长度参数,用于限定进行复制或者比较的字符数。因此我们可以主动的选择字符串中的字符数量进行操作,防止难以预料的长字符串从它们的目标数组溢出。

一、strncpy库函数

1.1函数介绍:

strncpy函数含有三个参数:

第一个参数是指向目标空间的起始地址 char* dest

第二个参数是源头字符串的起始地址 char* src

第三个参数是一个无符号整型,size_t count

返回类型为 char* 即返回目标空间字符串的起始地址。

该函数的作用为:

strncpy字符串将源头字符串的内容复制到目标空间中,但是并不全部复制,而是写入的字符数目由count指定,即向目标空间拷贝count个字符。

如果源头字符串的数目小于count,那么目标空间中将会用额外的 '\0' 填充到count个数目长度,如果源头字符串的数目大于或等于count,那么将只有count个字符拷贝到目标空间,结果将不会以'\0' 结尾

使用案例:

我们用调试的方法对下面代码进行分析

冰冰学习笔记:strncpy,strncat,strncmp_第1张图片

 当count为3时,说明我们要把arr2中xxx拷贝到arr1中,拷贝完是否为xxx\0defg\0

结果:

冰冰学习笔记:strncpy,strncat,strncmp_第2张图片

 结果显然不是,strncoy函数只是将指定的3个字符拷贝过去,并没有在字符结尾放'\0'

当我们把count改为4呢?

结果:

冰冰学习笔记:strncpy,strncat,strncmp_第3张图片

此时我们发现出现了'\0',原因是strncpy需要将arr2中的4个字符拷贝过去,而arr2中的第四个字符为'\0'所以会将其拷贝过去。

如果count为6呢,大于字符串arr2的字符长度,是否会填充'\0'到6个字符呢?

结果:

冰冰学习笔记:strncpy,strncat,strncmp_第4张图片

源头字符串虽然只有4个字符,但是count为6,所以strncpy函数将自动把2个字符变为'\0'以满足复制6个字符的要求。

这也就说明了strncpy函数复制的长度完全取决于count的大小 

还有一点,如果arr2字符串中间存在'\0',拷贝的时候遇到该'\0',strncpy也视为源头字符串到了结束标记,后面的内容将不会拷贝,而是补'\0'

冰冰学习笔记:strncpy,strncat,strncmp_第5张图片

拷贝后的字符串并不是"xxx\0xx",而是遇到'\0'后便不再拷贝,开始补'\0' 。

1.2模拟实现:

有了上面的分析,我们模拟实现起来便有了明确的思路。

大体逻辑还是和my_strcpy函数的模拟类似,用断言来避免传过来的空指针,加入const修饰来增强代码的鲁棒性,以及用ret指针来记录目标空间的起始地址,以便返回。

只是我们要注意到循环条件的终止应该取决于count的大小,而并非指针src指向字符的内容。

并且在src指向'\0'时,需要判断count是否为0,如果不是,我们要在目标空间补充'\0',是则跳出循环,返回起始指针。

因此代码的实现为下面的版本:

#include
char* my_strncpy(char* dest, const char* src, size_t n)
{
	assert(dest && src);
	char* ret = dest;//存放目标空间的起始地址
	while (n--)
	{

		if (*src == '\0')//*src字符个数小于n,后面填充'\0'
		{
			*dest++ = '\0';
		}
		else//将n个字符复制过去
		    *dest++ = *src++;
	}
	return ret;
}

根据n的大小来执行复制,如果src指向的内容并非为'\0'则将内容赋值给dest,然后两者向后移动,如果src指向'\0'并且count没有为0,则dest指针指向的内容赋值为'\0',并且dest继续向后移动。

最终循环结束函数返回起始指针ret。

冰冰学习笔记:strncpy,strncat,strncmp_第6张图片

二、strncat库函数

2.1函数介绍:

strncat函数和strncpy函数的参数一样,也是含有三个参数。

第一个参数为需要连接的字符空间的起始地址 char* dest

第二个参数为连接字符的源头空间的起始地址 char* src

第三个参数为连接的字符个数count,也为 size_t 类型 

返回类型依旧为char*类型,返回的是目标空间的起始地址

函数作用:

strncat函数的作用为将源头字符串src中的count个字符连接到dest字符串的末尾,最多复制的字符个数就为count。但是,strncat函数总是在结果字符串后面添加一个 '\0' ,而且不会像strncpy函数那样对目标数组用'\0'填充。

目标数组中原先的字符串并没有算在strncat的长度中。strncat最多向目标空间复制count个字符(再加一个结尾的'\0'),它才不管目标参数除去原先存在的字符串后空间够不够用,宛如一个愣头青。

使用案例:

冰冰学习笔记:strncpy,strncat,strncmp_第7张图片

使用strncat函数将arr2中的3个字符连接到arr1中,他会先找打arr1空间的'\0'作为连接的起始位置,然后将arr2中的三个字符从'\0'的位置开始存放,末尾会自动放上'\0'。

如果count超过arr2的字符数量呢,例如我们想把6个字符连接到arr1中,当然我们的arr1要保证足够大,否则将会溢出。

arr1[15]="xxxx\0xxxxxxxxx";

arr2[ ]="abcd"

冰冰学习笔记:strncpy,strncat,strncmp_第8张图片  

我们发现strncat函数只是将arr2中存在的所有字符复制过去,并不会自动用'\0'补齐到6个字符。

2.2模拟实现:

有了上面的分析,模拟实现起来就容易多了。

首先,我们需要找到目标空间的'\0'字符,因为这是连接的起始位置。找到很容易,我们只需要用一个while循环便可以做到,就像在模拟实现strcat函数时那样。

然后,我们要开始进行字符串的复制,从源头src复制到目标dest,但是我们要注意,他并不是总是复制,当count为0时结束,当src指向'\0'时也会结束,而且这两个条件有一个满足就会停止。并且,他不会自己补齐'\0'。

最后,strncat函数总是会在末尾补上一个'\0',也就是说一旦循环停止,立刻将'\0'放到目标字符串后面。

实现代码:

char* my_strncat(char* dest, const char* src, size_t n)
{
	assert(dest && src);
	char* ret = dest;//存放目标空间的起始地址
	while (*dest)//找到目标空间的'/0'
	{
		dest++;
	}
	while (n-- && *src)//*src为假说明源头字符串结束,n为假说明n个字符连接完毕
	{
		*dest++ = *src++;
		
	}
	*dest = '\0';//总是在最后放一个'\0'
	return ret;
}

当然此时也有人问为何不把*dest++=*src++写到while判断那里。就像模拟strcpy那样实现呢?

如果如此书写代码,将会产生一个错误。

如果count数目小于src指向的字符字符串个数的时候会导致不会在结尾放一个'\0'

简单,加上就是了。

将代码写成这样:

char* my_strncat(char* dest, const char* src, size_t n)
{
	assert(dest && src);
	char* ret = dest;//存放目标空间的起始地址
	while (*dest)//找到目标空间的'/0'
	{
		dest++;
	}
	while (n-- &&(* dest++ = *src++))
	{
		;//此代码只完成了连接,没有在后面放'/0'

	}
	*(dest) = '\0';//如果加上此句,将会产生多余的'/0'

	return ret;
}

但是又会产生另一个错误,在count大于src指向的字符内容时,他的确会在末尾加一个'\0'。但是,他并不会理会前面是否存在'\0'了,即就算src字符末尾的'\0'拷贝过来了,他还是会在后方放一个'\0'。

冰冰学习笔记:strncpy,strncat,strncmp_第9张图片

 因此,此代码不可取。

三、strncmp库函数

3.1函数介绍:

strncmp函数含有三个参数

str1和str2为两个待比较的字符串的起始地址。

count为比较的字符数,最多比较count个字符。

如果两个字符串在第count字符之前存在不相等的字符,该函数就会像strcmp一样停止比较,并返回结果;如果前count个字符相等,则返回0。

函数使用:

冰冰学习笔记:strncpy,strncat,strncmp_第10张图片 

前5个字符相等,返回0。、

冰冰学习笔记:strncpy,strncat,strncmp_第11张图片

此时虽然要求比较前7个字符,但是,arr2并没有这么多的字符,在地6个字符时,arr2指向的是'\0',与arr1中的 'f' 进行比较,结果是arr1>arr2,返回大于0的数字。 

3.2模拟实现:

该函数的模拟实现比较简单,就是在strcmp函数比较的基础上添加一个count字符的限制。

所以我们这样实现:

int my_strncmp(const char* s1, const char* s2, size_t n)
{
	assert(s1 && s2);
	while (n-- && *s1 == *s2)
	{
		if (n==0)
		{
			return 0;
		}
		s1++;
		s2++;
	}
	return *s1 - *s2;
}

你可能感兴趣的:(C语言笔记,学习,c语言,c#)