网络知识 娱乐 字符串函数和内存操作函数

字符串函数和内存操作函数

字符函数和字符串函数

  • 1、strlen函数
    • 1.1 strlen函数功能介绍
    • 1.2 模拟实现strlen函数
  • 2、strcpy函数
    • 2.1 strcpy函数功能介绍
    • 2.2 模拟实现strcpy函数
  • 3、strcat函数
    • 3.1 strcat函数功能介绍
    • 3.2 模拟实现strcat函数
  • 4、strcmp函数
    • 4.1 strcmp函数功能介绍
    • 4.2 模拟实现strcmp函数
  • 5、strncpy函数
    • 5.1strncpy函数功能介绍
  • 6、strncat函数
    • 6.1strncat函数功能介绍
  • 7、strncmp函数
    • 7.1 strncmp函数功能介绍
  • 8、strstr函数
    • 8.1 strstr函数功能介绍
    • 8.2 模拟实现strstr函数
  • 9、strtok函数
    • 9.1 strtok函数功能介绍
  • 10、strerror函数
    • 10.1 strerror函数功能介绍
  • 11、memcpy函数
    • 11.1 memcpy函数功能介绍
    • 11.2 模拟实现mencpy函数
  • 12、memmove函数
    • 12.1 memmove函数功能介绍
    • 12.2 模拟实现memmove函数
  • 13、memcmp函数
    • 13.1 memcmp函数功能介绍
  • 14、memset函数
    • 14.1 memset函数功能介绍
  • 15、总结

1、strlen函数

1.1 strlen函数功能介绍

strlen函数是求字符串长度的函数。

size_t strlen(const char* str);

strlen函数展示

#include 
#include 

int main()
{
	char arr1[] = "abcdef";//这种写法在字符串结尾自动添上了''
	char arr2[] = { 'a','b','c','d','e','f' };//这种写法求不了数组中储存的字符串长度,字符串结尾没有'',可以主动往里面放''
	char arr3[20] = { 'a','b','c','d','e','f' };  //可以求字符串长度,因为给定了数组空间大小,
	                                              //在存储未满时,后面自动填充了数字0,数字0对应空字符''
	int len1 = strlen(arr1);//字符串以''作为结束标志,strlen函数返回的是在字符串中前面出现的字符个数(不包含'')
	int len2 = strlen(arr2);
	int len3 = strlen(arr3);

	printf("%dn", len1);
	printf("%dn", len2);
	printf("%dn", len3);

	if (strlen("abc") - strlen("qwerty") > 0)
	{
		printf(">n");//strlen的返回值是无符号数,无符号数-无符号数还是无符号数,大于0
	}
	else
	{
		printf("<=n");
	}

	return 0;
}

打印结果
在这里插入图片描述

  1. 字符串以‘’作为结束标志,strlen函数返回的是在字符串中‘’前面出现的字符个数(不包含‘’)。
    数组arr1与数组arr3都有‘’所以能计算出字符串长度是6,数组arr2储存的字符串在结尾没有‘’,会一直往后找‘’,直到找到为止,计算出的字符串长度是随机值。
  2. 由上得出,函数参数指向的字符串必须要以‘’结尾。
  3. 为什么代码中的条件语句,得到的结果是 >号?
    函数的返回值为size_t,是无符号类型的。无符号数-无符号数还是无符号数,大于0

1.2 模拟实现strlen函数

#include 
#include 

size_t my_strlen(const char* str) //const保护指针所指向的内容不被改变
{
	int count = 0;
	assert(str != NULL);//当str等于空指针时,编译会报错
	while (*str)
	{
		str++;
		count++;
	}
	return count;
}
int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%dn", len);
	return 0;
}

打印结果
在这里插入图片描述

  1. 采用计数器的形式,对一个一个字符进行计数,直到找到空字符‘’停止计数。模拟实现strlen函数还有递归的方法、指针-指针的方法。
  2. 库函数assert()
    assert函数是一个断言函数,功能为:当括号中的条件不满足时,在程序进行编译的过程中会报错。
    为了避免传过来的是个空指针,而编译人员又没有发现,使用assert函数进行断言,assert(str != NULL); 或者这样写assert(str);
  3. const修饰指针变量
  1. const如果放在 * 的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变,但是指针变量本身的内容可以改变。
  2. const如果放在 * 的右边,修饰的是指针变量本身,保证了指针变量的内容不能修改,但是指针指向的内容可以通过指针改变。
  3. 我们的目的只是对字符数组中储存的字符串进行计数,为了避免改变指针指向内容,在参数用const对指针进行修饰。

2、strcpy函数

2.1 strcpy函数功能介绍

strcpy函数是对字符串进行拷贝的函数。

char* strcpy(char* destination, const char* source);

strcpy函数展示

#include 
#include 

int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "abcdef";
	char* arr3 = "fvgdf";//不能作为目标空间,因为要求目标空间可变,但是这是一个常量字符串,常量是不可修改的
	char arr4[] = "123456";

	strcpy(arr1, arr2);  //源字符串arr2的''也会拷贝过去,所以要求源字符串必须有''。
	strcpy(arr4, arr2);

	printf("%sn", arr1);
	printf("%sn", arr4);
	return 0;
}

打印结果
在这里插入图片描述

  1. 源字符串必须以‘’结束,会将源字符串中的‘’拷贝到目标空间。
  2. 目标空间必须足够大,以确保能存放源字符串。
  3. 目标空间必须可变。
    arr1与arr4作为目标空间都是可变的,arr3是一个指针,指向常量字符串“abcdef”,常量字符串不可变,所以arr3不能作为目标空间。

2.2 模拟实现strcpy函数

#include 
#include 

char* my_strcpy(char* dest, const char* src)
{
	assert(src && dest);//这两个指针都不能为空
	char* ret = dest;
	while (*src)
	{
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src;//将最后一个''也拷贝过去。
	return ret;
}

int main()
{
	char arr1[20] = { 0 };
	char* arr2 = "hello bit";

	my_strcpy(arr1, arr2);
	printf("%sn", arr1);
	printf("%sn", my_strcpy(arr1, arr2));//l链式访问,函数的返回值直接作为另一个函数(printf函数)的参数
	return 0;
}

打印结果
在这里插入图片描述

  1. strcpy函数返回的是目标空间的起始位置。
  2. 使用库函数对目标指针与源指针进行断言,避免出现传参过来的是空指针。
  3. 为什么要设置返回类型,而不是设置成void*?
    有了返回值才能实现链式访问。
  4. 什么是链式访问?
    把一个函数的返回值作为另外一个函数的参数。如:
    printf(“%sn”, my_strcpy(arr1, arr2));my_strcpy函数的返回值直接作为 printf函数的参数。给 printf函数一个字符串的起始位置就是一次向后进行打印,直到‘’。
  5. 当指针访问到源字符串的‘’时,循环中止。为了将‘’拷贝过去,再跳出循环后,还要再进行拷贝一次。

还能对上述my_strcpy函数进行优化

#include 
char* my_strcpy(char* dest, const char* src)
{
	assert(src && dest);//这两个指针都不能为空
	char* ret = dest;
	while (*dest++ = *src++)//更为简便的写法,最后的''也能拷贝过去
	{
		;
	}
	return ret; //strcpy函数返回的是目标空间的起始位置。
}
  1. while (* dest++ = * src++)
    每一次循环得到的结果是赋值后的结果,也就是 *dest,当指针访问到源字符串的 ‘’ 时,先将源字符串的 ‘’ 拷贝过去,此时循环条件 *dest== ‘’ ,循环中止。一次性将 ‘’ 拷贝到了目标字符串。

3、strcat函数

3.1 strcat函数功能介绍

strcat函数是字符串追加函数,在一个字符串的结尾 ‘’处开始进行追加其他字符串。

char* strcat(char* destination, const char* source);

strcat函数展示

#include 
#include 

int main()
{
	char arr1[20] = "hello";
	char arr2[] = " bit";
	strcat(arr1, arr2);//从目标字符串的‘’处开始追加,源字符串的‘’也会追加过去
	printf("%sn", arr1);

	return 0;
}

打印结果
在这里插入图片描述

  1. 源字符串必须以‘’结束,目标字符串也得有‘’,从目标字符串的‘’处开始追加,目标字符串 ‘’被覆盖。
  2. 目标空间必须右足够的大,能容纳下源字符串的内容。
  3. 目标空间必须可修改,即不能是常量字符串。
  4. 字符串不能自己给自己追加,即 strcat(arr2, arr2); ,会陷入死循环,为什么?
    在这里插入图片描述
    如上图所示,当源字符串开始往目标字符串的 ‘’ 开始一一拷贝完毕以后,源字符串要将自己的 ‘’ 也拷贝过去,用来结束拷贝。因为源字符串与目标字符串是同一块字符串,之前的 ‘’处已经被字符 ‘b’覆盖了。所以拷贝不会停止,进入死循环。

3.2 模拟实现strcat函数

#include 
char* my_strcat(char* dest, char* src)
{
	char* ret = dest;
	assert(dest && src);
	while (*dest)
	{
		dest++;   //找目标空间的‘’
	}
	while (*dest++ = *src++)//从目标空间的‘’处开始拷贝
	{
		;
	}
	return ret;  //strcat函数返回的是目标空间的起始位置。
}
  1. strcat函数本质上可以认为是一个拷贝函数,只不过要先找到目标函数的结尾 ‘’ 处,从目标函数的结尾 ‘’ 处开始拷贝。
  2. strcat 函数的返回值是目标函数的起始位置,所以返回类型是 char*。

4、strcmp函数

4.1 strcmp函数功能介绍

strcmp函数是字符串比较函数,比较的是两个字符串中对应字符的ASCII值。

int strcmp(const char* str1,const char* str2);

strcmp函数展示

#include 
#include 

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abq";
	int ret = strcmp(arr1, arr2);//返回值是大于0,小于0,等于0的整数

	if (ret > 0)
	{
		printf(">0n");
	}
	else if (ret == 0)
	{
		printf("==n");
	}
	else
	{
		printf("<0n");
	}
	
	return 0;
}

打印结果
在这里插入图片描述

  1. 如何判断两个字符串的大小?
    两个字符串的大小比较的是两个字符串中对应字符的ASCII值,如果相同,就比较下一对,直到不同或者都遇到 ‘’, ‘’也要参与比较。如代码中的两个字符串前两个字符对应相等,第三个字符‘c’的ASCII值小于字符‘q’,所以arr1中的字符串小于arr2中的字符串。
  2. 第一个字符串大于第二个字符串,则返回大于0的数字。
  3. 第一个字符串等于第二个字符串,则返回0。
  4. 第一个字符串小于第二个字符串,则返回小于0的数字

4.2 模拟实现strcmp函数

#include 
int my_strcmp(const char* s1, const char* s2)
{
	assert(s1 && s2);
	while (*s1 == *s2)
	{
		if (*s1 == '')
		{
			return 0;
		}
		s1++;
		s2++;
	}
	if (*s1 > *s2)
	{
		return 1;
	}
	else
	{
		return -1;
	}
}
  1. 目标字符串与源字符串都不能改变,所以加上const修饰。
  2. 避免目标字符串的指针与源字符串的指针为空指针,所以对其断言。
  3. 在这里,将返回值设定成了 -1、0、1。

strcpy函数、strcat函数、strcmp函数都是长度不受限制的字符串函数。下面分别介绍strncpy函数、strncat函数、strcnmp函数,这些是长度受限制的字符串函数,即可以规定长度进行拷贝、追加、比较。

5、strncpy函数

5.1strncpy函数功能介绍

类比strcpy函数,不过多了一个参数----->要拷贝几个字符的个数。

char* strncpy(char* destination,const char* source,size_t num);

strncpy函数展示

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "qwertyuiop";

	strncpy(arr1, arr2, 6);//可以指定拷贝的字符个数
	printf("%sn", arr1);
	return 0;
}

打印结果
在这里插入图片描述

  1. 拷贝了6个字符从源字符串到目标空间

如果源字符串的长度小于num个,则拷贝完源字符串之后,在目标的后边追加0,直到num个。如下所示

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "qwe";

	strncpy(arr1, arr2, 6);//要拷贝的字符个数大于字符串中字符真正的个数,后面自动填充‘’,直到6个。
	printf("%sn", arr1);
	return 0;
}

在这里插入图片描述

  1. 前面三个 ‘’,是为了满足六个字符,最后一个 '’是自动添加的,用来结尾。

6、strncat函数

6.1strncat函数功能介绍

类比strncat函数,不过多了一个参数----->要追加几个字符的个数。

char* strncat(char* destination,const char* source,size_t num);

strncat函数展示

#include 
#include 

int main()
{
	char arr1[20] = "abcdef";
	char arr2[] = "qwertyuiop";
	strncat(arr1, arr2, 5);//追加5个字符过去,同时‘’也会追加过去。当追加的个数大于源字符串的字符个数时,只会补加一个‘’。
	printf("%sn", arr1);

	return 0;
}

打印结果
在这里插入图片描述

  1. 追加5个字符过去,同时‘’也会追加过去。
  2. 当追加的个数num,大于源字符串的字符个数时,不会追加num个 ‘’ ,只会补加一个 ‘’ 用以结尾。

7、strncmp函数

7.1 strncmp函数功能介绍

类比strcmp函数,不过多了一个参数----->要比较几个字符的个数。

int strncmp(const char* str1,const char* str2,size_t num);

strncmp函数展示

#include 
#include 

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcdq";
	char arr3[] = "qwertyuiop";
	int ret = strncmp(arr1, arr2, 4);
	int rets = strncmp(arr1, arr3, 4);
	printf("%dn", ret);
	printf("%dn", rets);
	return 0;
}

打印结果
在这里插入图片描述

  1. arr1与arr2比较前4个字符,相等。
  2. arr1与arr3比较前4个字符,arr1储存的字符小于arr3储存的字符。

下面介绍字符串查找函数

8、strstr函数

8.1 strstr函数功能介绍

strstr函数是子串查找函数,看str2是不是str1的子串,是的话,返回的是str2在str1中第一次出现的地址,如果不是,返回空指针。

char* strstr(const char* str1 ,const char* str2);

strstr函数展示

#include 
#include 

int main()
{
	char arr1[] = "abcdefabcdef";
	char arr2[] = "cdef";
	char* ret = strstr(arr1, arr2);//找不到,返回空指针。找到了,返回的是子串第一次出现的地址。
	if (NULL == ret)
	{
		printf("找不到子串");
	}
	else
	{
		printf("%sn",ret);
	}
	return 0;
}

打印结果
在这里插入图片描述

  1. cdef是子串,返回第一次出现在arr1中的地址,即第一个c的地址,printf函数根据返回的地址,依次向后打印字符串。
  2. 如果不是子串,返回空指针。

8.2 模拟实现strstr函数

#include 
char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	const char* s1 = str1;
	const char* s2 = str2;

	const char* cur = str1;
	while (*cur)
	{
		s1 = cur;
		s2 = str2;

		while (*s1 && s2 && (*s1 == *s2))
		{
			s1++;
			s2++;
		}
		if (*s2 == '')
		{
			return (char*)cur;   //找到了
		}
		cur++;
	}
	return NULL; //找不到
}

在这里插入图片描述

  1. 将str1与str2分别赋给s1与s2,利用s1与s2往字符串后面遍历查找相同的部分。
  2. cur 是用来记录str1与str2字符串第一次出现相等字符时的位置,等找到子字符串后,用来作为返回值。
  3. 如图所示,第一次进入,在两个字符串的起始位置,字符不相等,所以cur不会在这记录,cur++;
  4. 第二个while循环是用来往后找字符串相同的部位的。第二个while循环的循环条件首先要保证 *s1与 *s2不为空字符,为空字符的话,说明s1或者s2已经找到字符串结尾了,循环中止。*s1与 *s2指向的字符不相等,循环也要中止。
  5. 如果第二个while循环跳出来后,判断一下 *s2是否为空字符,为空字符的话,说明s2已经遍历一遍,s2中所有的字符都有在s1中被找到。此时s2中储存的字符串是s1的子字符串。
  6. 第二个while循环跳出来后,并且又判断没有找到,将cur记录的位置进行修正,往后走一步,cur++;,此时第一个while进行第二次循环,将cur的位置赋值给s1,s1被修正(s1=cur++,说明s1此时的位置是上次开始记录位置的后一个,从这里开始重新与s2进行比对),再将s2的起始位置修正回来,重新进行比对。
  7. 如果 cur 都往后遍历到空字符了,还是没有找到,说明s2不是s1的子字符串,返回空指针NULL。
  8. 函数参数的设定都用const修饰了,所以 *s1与 *s2与 *cur都要用const修饰,使得 = 左右两边变量的权限是保持一致的,不然 = 右边的变量收到const修饰,= 左边的变量不受到const修饰。
  9. 返回类型是char* ,返回值是记录位置,cur之前被const所修饰了,此时要改回来,强制类型转换----->char*。

9、strtok函数

9.1 strtok函数功能介绍

char* strtok(char* str,const char* sep);

  • sep参数是个字符串,定义了用作分隔符的字符集合
  • 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
  • strtok函数找到str中的下一个标记,并将其用 ‘’ 结尾,返回一个指向这个标记的指针。
  • strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容,并且可修改。
  • strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
  • strtok函数的第一个参数为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
  • 如果字符串中不存在更多的标记,则返回NULL指针。

strtok函数展示

#include 
#include 

int main()
{
	char arr[] = "zhengguobin@163.com";
	char buf[30] = { 0 };
	strcpy(buf, arr);
	const char* sep = "@.";

	printf("%sn",strtok(buf, sep));//只找第一个标记
	printf("%sn", strtok(NULL, sep));//是从保存好的开始继续往后找
	printf("%sn", strtok(NULL, sep));
	return 0;
}

打印结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 由打印结果可见,字符串根据分割标记被分割了。
  2. 如果再添加一个printf(“%sn”, strtok(NULL, sep));,则会打印(NULL)。因为返回值是 NULL。

上面的打印过程可以优化,不用写三个打印函数。如下

#include 
#include 

int main()
{
	char arr[] = "zhengguobin@163.com";
	char buf[30] = { 0 };
	strcpy(buf, arr);
	const char* sep = "@.";
	char* str = NULL;
	for (str =