C语言----详解字符串相关的库函数(建议收藏)

2023-05-16




文章目录

  • 系列文章目录
  • 前言
  • 一、C语言相关字符串库函数一览表
  • 二、strlen函数
  • 三、strcpy函数
  • 四、strcat函数
  • 五、strcmp函数
  • 六、strncpy函数
  • 七、strncat函数
  • 、strncmp函数
  • 九、strstr函数
  • 十、strtok函数
  • 十一、strerror函数
  • 十二、memcpy函数
  • 十三、memmove函数
  • 十四、memcmp函数
  • 十五、memset函数

前言

C语言的库函数,是我们经常在编写程序所用到的函数,我们可以借用库函数去实现各种各样的功能,在本篇文章,我们介绍的是C语言中字符串和字符的相关库函数,以及他们的模拟实现,通过模拟实现我们可以深入了解到库函数的工作原理,以便今后更好的使用,下面开始我们的库函数讲解!!!


一、C语言相关字符串库函数一览表

求字符串长度strlen
长度不受限制的字符串函数strcpy   strcat   strcmp
长度受限制的字符串函数strncpy  strncat  strncmp
字符串查找strstr  strtok
错误信息报告strerror
内存操作函数memcpy  memmove  memset  memcmp


二、strlen函数(求字符串长度)




1.所需头文件




2.参数类型

我们这里看到这里需要的参数类型是char*所以我们传进来的参数需要是字符串的地址,并且我们可以看到这里的指针变量前加了const的修饰,这样就可以保证传进来的字符串不被修改,因为我们求字符串的长度是不需要对字符串进行修改的,加以const修饰保证程序更加的安全。

3.返回值类型

我们看到图片上面的返回值类型为size_t类型,我们知道size_t类型为无符号整型,我们也易于理解,毕竟求出一个字符串的长度肯定是一个正数,所以这里的返回值类型为一个无符号整型

这里我引入一个例子:

#include <stdio.h>
#include <string.h>
int main()
{
	char* str1 = "abc";
	char* str2 = "abcedf";
	if (strlen(str1) - strlen(str2) > 0)
	{
		printf("hehe\n");
	}
	else
	{
		printf("haha\n");
	}
	return 0;
}

 我们可以看到,字符串str1的长度明显小于字符串str2的长度,所以相减后应该<0打印的内容是:haha,但是,实际情况是打印了hehe,这就是我们返回值类型的原因。因为返回值是无符号整型,两个无符号整型相减得到的还是无符号整型,这里就把-3当成一个正数,又因为正数原返补码都一样,内存中存储的是补码,-3的补码

所以这里就把-3当做一个正数,最高位的1不再是符号位,而是有效位,所以把-3的补码转换后,得到的是一个很大的正数,并且一定是大于0的,所以这里打印出来的是hehe

4.strlen函数的功能及其注意事项

#include <stdio.h>
#include <string.h>
int main()
{
	char* str1 = "abcd";
	int ret = strlen(str1);
	printf("%d", ret);
	return 0;
}

我们看到这就是strlen函数的功能,即求字符串的长度,这里的长度是不算‘\0’的,所以求一个字符数组的长度时,一定要规定‘\0’的位置。这里求出来的字符串长度就是我们可以看到的这4个字符

注意事项: 

①.字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0' )。  

②.参数指向的字符串必须要以 '\0' 结束。

③.注意函数的返回值为size_t,是无符号的( 易错 )

5.模拟实现strlen函数

方法一:计数器方法(访问每一个字符不是'\0'计数器就+1)

#include <stdio.h>
#include <string.h>
int my_strlen(const char* str)
{
	int count = 0;
	while (*str++!='\0')
	{
		count++;
	}
	return count;
}
int main()
{
	char* str1 = "abcdef";
	int ret = my_strlen(str1);
	printf("%d", ret);
	return 0;
}

方法二:(指针-指针,字符串末地址 - 字符串首地址得到的结果就是元素个数)

#include <stdio.h>
#include <string.h>
int my_strlen(const char* start, int sz)
{
	char* dest = &start[sz - 1];
	int count = dest - start;
	return count;
}
int main()
{
	char str1[] = "abcdef";
	int sz = sizeof(str1) / sizeof(str1[0]);
	int ret = my_strlen(str1, sz);
	printf("%d", ret);
	return 0;
}

方法三:递归方法

#include <stdio.h>
#include <string.h>
int my_strlen(const char* p)
{
	static int count = 0;//定义一个计数器  由于这里使用了递归  所以这里使用静态变量保持计算器不变可以一直累加字符串的个数
	if (*p == '\0')
	{
		return count;
	}
	if (*p != 0)
	{
		count++;
		return my_strlen(p + 1);//把下一个字符的地址传进去
	}
	
}
int main()
{
	char* str = "abcdef";
	int ret = my_strlen(str);
	printf("%d", ret);
	return 0;
}


三 、strcpy函数(字符串拷贝)

1.所需头文件

2.参数类型

我们可以看到参数类型是两个字符指针类型,所以我们可以知道,这里是需要把两个字符串的地址传进去 。第一个参数是需要被改变(拷贝)的字符串地址,我们用destination(目及地)来表示。第二个参数存储的地址是要拷贝的内容,我们用source表示。这里我们用const修饰source,因为这里的内容不需要改变,利用const修饰保证程序的安全性。

3.返回值类型

我们可以看到这里的返回值类型为char*类型,所以返回的是被拷贝完成后的字符串地址

4.strcpy函数的功能及其注意事项

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

我们看到strcpy函数在这里的作用就是把str2的内容拷贝到了str1中。

注意事项:

①.源字符串必须以 '\0' 结束。(没有'\0'strcpy函数就不知道什么时候拷贝结束了)

 ②.会将源字符串中的 '\0' 拷贝到目标空间。

③.目标空间必须足够大,以确保能存放源字符串。(防止造成越界访问)

 ④.目标空间必须可变。(目标空间不能被const修饰)

5.模拟实现strcpy函数

逐条语句解析:

①~②.定义两个字符串

③.将str1和str2的地址传入我们自己编写的模拟拷贝函数,用一个指针类型变量ret去接收拷贝后的变量地址。

④.这里我们模拟strcpy函数的传入参数样式,定义两个字符指针去接收变量的地址,返回值是char*类型

⑤.这里利用assert去判断一下str1和str2是不是空指针,防止造成野指针,导致程序运行出错

 在这里简单介绍一下assert函数:

a.所需要的头文件

b.参数类型

 这里的参数我们把判断字符串是不是空指针的表达式传进去

c.返回值类型

如图我们看到返回值类型是空类型,所以不需要去定义变量接收返回值

⑥.定义一个指着变量去存储被拷贝后的字符串(目标字符串),便于一会返回拷贝后的字符串地址。

⑦.利用循环把str2的内容拷贝到字符串str1中,这里简化了代码,把++放到了赋值语句中,这里需要注意我们要把++放到dest以及src的后面,这样保证先正常赋值第一个字符,再向后推移继续拷贝

⑧.最后把拷贝完的字符串返回回去

图解代码:

 运行结果:


四、strcat函数(字符串追加)

1.所需头文件 

2.参数类型

我们看到这里的参数类型同strcpy函数的参数类型一致,两个指针分别接收的地址是:被追加的目标字符串,和需要追加的内容,内容这里被const修饰,保证内容不被修改,提高程序的安全性。

3.返回值类型

我们看到strcat函数的返回类型为char*类型,这样是为了返回被追加后的字符串地址。

4.strcat函数的功能及其注意事项

#include <stdio.h>
#include <string.h>
int main()
{
	char str1[20] = "abcd";
	char str2[] = "efgh";
	char* ret = strcat(str1, str2);
	printf("%s", ret);
	return 0;
}

 我们看到strcat函数就是把str2的内容追加到了str1的末尾,从str1的‘\0’开始追加字符串str2的内容。

注意事项:

①.源字符串必须以 '\0' 结束。(没有结束标志会导致程序崩溃,因为程序不知道追加到哪里停止)

②.目标空间必须有足够的大,能容纳下源字符串的内容。

我们看到的str1的长度为5,明显只可以容纳str1的内容,不能再继续容纳str2的三个字符,这样会导致越界访问,造成程序的崩溃  

③.目标空间必须可修改。 (我们需要对目标空间进行字符串的追加,所以目标空间一定可以被修改,目标空间不能被const修饰)

5.模拟实现strcat函数

逐条语句解析:

①~②.定义两个字符串,目标字符串规定好大小,保证可以容纳追加的字符串

③.把两个字符串的地址传入我们自己编写的strcat函数,定义一个字符指针变量ret来接收strcat返回的字符串地址

④.模仿strcat函数的返回类型以及参数类型

⑤.定义一个字符指针存储目标字符串的地址,便于一会返回被追加后的目标字符串地址。

⑥.利用while循环把dest指针指向字符串末尾的'\0',因为第二个字符串追加到目标字符串是从目标字符串的'\0'开始追加的。

⑦.利用循环从dest的末尾位置开始,把src对应的地址内容赋值给目标字符串,两个地址都是后置++,先正常把str2的第一个字符赋值给str1,再++向后推移继续将str2中的字符赋值给目标字符串。

⑧.返回被追加后的目标字符串地址。

图解代码:

 运行结果展示:


 五、strcmp函数(字符串比较)

1.所需头文件

2.参数类型

这里是两个字符指针,用来接收两个字符串的地址,我们看到这两个指针都被const修饰,因为strcmp函数是用来比较两个字符串大小的(注意:这里比较的是字符串的内容,不是比较的字符串个数),所以不需要对字符串进行修改,这里用cosnt修饰,保证程序的安全性。

3.返回类型

我们看到strcmp函数的返回类型是int类型,我们看一下strcmp函数对返回值的规定:

 我们看到strcmp的返回值都是整型数据,所以这里定义一个int类型作为函数的返回类型

4.strcmp函数的功能

#include <stdio.h>
#include <string.h>
int main()
{
	char str1[] = "abcd";
	char str2[] = "abc";
	int ret = strcmp(str1, str2);
	printf("%d", ret);
	return 0;
}

我们看到这里得到的是 1 是一个>0的数值,证明字符串str1比字符串str2要大,我们看str1和str2的前三个字符串相等,从四个字符开始str1是‘d’,而str2是‘\0’,我们知道字符d的ASSIC码值大于字符0的ASSIC码值,只要有一组字符串存在大小的差异,就停止比较返回数值,所以我们可以判断出来字符串str1是大于str2的

相等的情况:(两个字符串一样)

小于的情况 :(返回小于0的数值)

5.模拟实现strcmp函数

 逐条语句解析:

①~②.定义两个字符串

③.将两个字符串的地址传进我们自己编写的my_strcmp函数内,定义一个整型变量ret去接收返回 的数值

④.模拟strcmp函数的参数类型和返回值类型的编写方法

⑤.判断传入函数内的两个字符串地址是否有效

⑥~⑨.从两个字符串的首元素开始比较,先判断字符串str1是不是空如果是空直接就返回0,如果两个字符相等,两个字符串首地址进行++操作后移继续比较,比到最后一个字符串如果还是相等返回最后两个字符对应ASSIC码值相减后的结果,因为两个字符串相等所以结果就是 0 。如果在比较过程中就发现了两组字符存在大小差异,即结束判断,返回两个字符对应ASSIC相减的结果,>0就是str1大于str2,<0就是str1小于str2。

图解代码:

  运行结果展示:


六、strncpy函数(受限制的字符串拷贝)

1.所需要的头文件

 2.参数类型

这里的前两个类型和strcpy函数的参数类型一致,一个指针存放目标字符串地址,一个指针存放待拷贝的字符串地址,这里的参数多了一个size_t类型(无符号整型)的count,是用来控制拷贝字符个数的,这里的count是几就从source中拷贝几个字符串到destination中

3. 返回值类型

这里的返回值类型是char*类型,返回的是被拷贝后的目标字符串的地址

4.strncpy函数的功能及其注意事项

#include <stdio.h>
#include <string.h>
int main()
{
	char str1[20] = "abcd";
	char str2[] = "kpl";
	char* ret = strncpy(str1, str2, 2);
	printf("%s", ret);
	return 0;
}

我们看到我们在strncpy函数中,前两个参数是字符串的地址,第三个参数我们写的是 2 ,所以就是把字符串str2的前两个字符拷贝到str1中。 

注意事项:同strcpy函数注意事项

5.模拟实现strncpy函数

这里的模拟实现只有在循环的位置(①和②的位置)进行了稍微的改变,把我们的需要进行拷贝的元素个数作为循环的变量, 拷贝几个字符,循环几遍就可以了,其他代码和strcpy的编写是一样的。


七.strncat函数(长度受限制的字符串追加)

1.所需要的头文件

2.参数类型 

 这里的前两个类型和strcat函数的参数类型一致,一个指针存放目标字符串地址,一个指针存放待拷贝的字符串地址,这里的参数多了一个size_t类型(无符号整型)的count,是用来控制追加字符个数的,这里的count是几就从source中追加几个字符串到destination中

3.返回类型

这里的返回值类型是char*类型,返回的是被追加后的目标字符串的地址

4.strncpy函数的功能及其注意事项

#include <stdio.h>
#include <string.h>
int main()
{
	char str1[20] = "abcd";
	char str2[] = "efgh";
	char* ret = strncat(str1, str2, 2);
	printf("%s", ret);
	return 0;
}

我们上图看到的代码在strncat中前两个参数是字符串的地址,第三个参数是拷贝的字符个数,这里写的是2所以把str2前两个字符拷贝到str1中

注意事项:同strncat函数

5.模拟实现strncat函数

我们看到这里的模拟实现(①和②的位置),对比strcat的模拟实现进行了小小的变动,同上面的strncpy函数的模拟实现,参考上面的解释即可。


八、strncmp函数(长度受限制的字符串拷贝)

1.所需头文件

2.参数类型 

我们看到strncmp函数的前两个参数是需要进行比较的两个字符串,第三个是比较的字符个数,count是几就比较几个字符串

3.返回类型

我们看到的返回类型是int类型(整型类型),这里返回的就是我们判断大小后的数值,>0/=0/<0的三种情况

4.strncmp函数的功能及其注意事项

#include <stdio.h>
#include <string.h>
int main()
{
	char str1[] = "abcd";
	char str2[] = "abkl";
	int ret = strncmp(str1, str2, 2);
	printf("%d", ret);
	return 0;
}

 我们看到,在strncmp函数中前两个是需要进行比较的字符串地址,第三个参数是需要进行比较的字符个数,我们看到这里需要进行比较的字符串个数是2,即比较str1和str2的前两个字符,我们看到str1和str2的前两个字符完全一致,所以返回的是 0。

 当比较三个字符的时候,明显str2的第三个字符是k,而str1的第三个字符是c,可以知道字符k的assic码值是大于字符c的,所以这里返回的是-1。

5.模拟实现strncmp函数

这里的strncmp函数的模拟实现,我们在循环变量进行了改动,这里用的是前置--,因为我们的首地址就是第一个元素,所以进到进入到 if 语句就比较的是第二个元素,因为如果第一个字符间存在大小差异,就不会进入 if 语句中,直接返回两个字符相差的assic码值即可,所以这里的count先行进行--,少比较一次即可。


 九、strstr函数(字符串查找函数)

1.所需头文件

2.参数类型 

我们可以看到参数是两个指针,分别接收的是被查找的字符串地址和查找内容的字符串地址,这两个指针都被const修饰,因为我们知道这里的两个字符是用来进行查找操作的,所以不需要对两个字符进行修改,用const修饰保证程序的安全性。

3.返回类型

我们看到这里的返回类型是char*类型(字符指针),所以这里接收的是字符string被strcharset查找到后,对应查找到的字符串首地址。

4.strstr函数的功能及其注意事项

#include <stdio.h>
#include <string.h>
int main()
{
	char str1[] = "abcdefdgh";
	char str2[] = "d";
	char* ret = strstr(str1, str2);
	printf("%s", ret);
	return 0;
}

这里我们看到strstr中接收的是我们两个字符串的地址,定义了一个字符指针变量ret去接收函数返回的地址,我们看到str2中只有一个字符d,我们在str1进行查找看是否也有字符串d,明显我们看到str1中第四个字符是d,所以停止查找返回第一次见到字符d所对应的地址,最后打印出来的是str1中字符d第一次出现后的地址所对应内容,注意str2一定要是str1的子字符串,这样才可以查找,否则是找不到的

找不到的情况:

①.

 我们看到str2中字符d在str1中可以查找到,但是q查找不到,所以查找失败,返回的是空指针(NULL) 

②.

我们看到这次str2中的两个字符在str1中都可以查找到,但是返回的也是空指针,是因为字符df在str1中不是连续的,这样导致str2并不是str1的子字符串,所以导致查找失败返回的是空指针(NULL)。

5.模拟实现strstr函数

 逐条语句解析:

①~②.定义两个字符串,用于进行查找操作

③.将两个字符串的地址传入我们编写好的模拟strstr函数中,定义一个指针变量用于接收函数返回的查找到的字符地址

④.模拟strstr函数的参数以及返回类型的定义方式,参数是两个指针用于存放两个字符串的地址,返回类型是字符指针,因为返回的是字符串的地址

⑤.判断两个字符串地址的有效性

⑥~⑦.定义两个指针分别用来存放字符串str1和字符串str2的地址,便于一会进行查找操作

⑧.用于记录,字符串str2中的字符在str1中的出现情况,cp指针用来记录重复字符首次出现的位置

⑨.如果是str2传进来的是空字符串直接返回字符串str1(这是一种特殊情况)

⑩.因为cp是记录字符出现的位置的指针变量,所以把cp当做循环的条件,如果cp==0就证明,以及已经查找到字符串str1的末尾了,所以循环就应该结束了

⑪~⑯.循环开始的时候cp和s1都是指向字符串str1的首地址,s2用于指向str2的首地址,cp是用于记录查找位置的指针,所以s1的每一次查找都是从cp开始,在里面继续嵌套循环循环条件是s1和s2都不能指向字符串结尾的‘\0’,因为如果指向‘\0’证明字符串都访问了一遍,这时候就可以判断出是否查找到,然后当遇到相等的情况时,s1和s2指针分别++再一次判断下一个字符是不是也相等,如果不相等跳出循环,cp进行+1操作指向str1的下一个字符,再次把cp的地址赋值给s1,s2重新指向字符串str2的首地址,进行下一次的查找操作。如果两个字符串在查找过程中一直满足 if 语句的条件,没有终止while循环,直到因为s2指向str2末尾的‘\0’导致循环终止,这就证明str2是str1的一个子字符串,这时就把cp指向的地址返回(cp记录的是两字符串开始匹配的位置)。如果都不满足这些情况,结尾处返回空指针,即没有查找到

 ⑰~⑱.判断返回的情况是打印找不到(返回值是NULL)或者打印查找到的字符串地址对应的字符串

图解代码:

   运行结果展示:


十、strtok函数 (字符串分割函数)

1.所需头文件

 2.参数类型

我们可以看到这里的参数是两个字符指针,第一个是用来存放待分割的字符串地址,第二个是用来存放分割符的集合地址

3.返回类型

返回类型是char*类型,因为我们需要返回分割后字符串的地址

4.strtok函数的功能及其注意事项

#include <stdio.h>
#include <string.h>
int main()
{
	char str1[] = "by@bite.cn";
	char str2[200] = { 0 };
	char sep[] = "@.";
	strcpy(str2, str1);
	char* ret = NULL;
	for (ret = strtok(str2, sep); ret != NULL; ret = strtok(NULL, sep))
	{
		printf("%s ", ret);
	}
}

  

注意事项:

①.sep参数是个字符串,定义了用作分隔符的字符集合

②.第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标 记。

③.strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注: strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。)

④.strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串 中的位置。

⑤.strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标 记。

⑥.如果字符串中不存在更多的标记,则返回 NULL 指针。

由于strtok函数使用情况较少,所以这里没有对strtok进行复写


十一、strerror(错误信息报告)

1.所需要的头文件

 2.参数类型及返回值类型

 返回错误码,所对应的错误信息。

3.strerror函数的使用

#include <stdio.h>
#include <string.h>
int main()
{
    FILE* pFile;
    pFile = fopen("unexist.ent", "r");
    if (pFile == NULL)
        printf("Error opening file unexist.ent: %s\n", strerror(errno));
    //errno: Last error number
    return 0;
}

 这里我们看到这里返回的就是错误码对应的错误原因。


十二、memcpy函数(内存拷贝函数)(内存不重叠)

1.为什么有内存拷贝函数

因为上方我们的拷贝函数strcpy和strncpy都是专门拷贝字符串的函数,所以只能拷贝字符串,这样就需要一个可以拷贝其他数据类型的函数,即我们的memcpy函数

2.所需头文件

 3.参数类型

两个void*指针(因为不知道具体拷贝的数据类型,利用void*指针可以接收任意类型的指针),两个指针分别接收待拷贝字符串的地址,一个接收待拷贝内容的字符串地址,第三个参数是size_t(无符号整型)的count,这里的count是整个需要拷贝的元素大小(单位:字节)

4.返回类型

我们看到返回类型是void*,返回的是拷贝后的地址

5.memcpy函数的功能及其注意事项

#include <stdio.h>
#include <string.h>
int main()
{
	int arr1[100] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[100] = { 0 };
	int i = 0;
	memcpy(arr2, arr1, 10 * sizeof(int));
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

                                                                                           

我们看到这里就是把arr1中的10个元素拷贝到了arr2中,memcpy的三个参数分别是两个数组的首地址,和数组arr1的待拷贝元素个数的大小(单位:字节)

注意事项: 

①.函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。

②.这个函数在遇到 '\0' 的时候并不会停下来。

③.如果source和destination有任何的重叠,复制的结果都是未定义的。

6.模拟实现memcpy函数

  逐条语句解析:

①.定义两个数组arr1和arr2

②.在我们自己编写的my_memcpy函数中传入arr2和arr1的地址,和需要进行拷贝的元素大小(单位:字节)

③.模拟memcpy函数的参数定义方式以及返回类型,这里由于我们不知道需要进行拷贝的元素类型,所以我们这里用void*指针进行接收,因为void*指针可以接受任意类型的指针,又因为对于src的内容我们不需要改变所以利用const进行保护,这里的元素大小利用size_t类型(无符号整型)来存储,因为元素个数都是正数,所以这里的类型采用无符号整型

④.判断两个数组地址的有效性

⑤.定义一个void*类型的指针用来存放目标字符串,便于一会拷贝完成后,返回其地址

⑥.以元素的大小作为循环的条件,由于我们不知道元素的具体类型,所以我们采用最细致的方法进行元素的访问,每次-1个字节去访问元素的每一位字节

⑦.我们知道char*类型一次可以访问1个字节,这是最细致的访问方法,所以我们把我们的目标字符串和待拷贝的字符串地址都强制类型转换为char*类型,这样便于我们遍历整个元素,这样一个字节一个字节的把str2的内容拷贝给str1

⑧.我们把dest和src强制类型转换为char*类型后,每+1就是跳过一个字节,这样保证每一位都可以进行交换,保证元素交换成功

⑨.返回拷贝后的目标字符串地址

⑩.打印出来数组的元素

图解代码:

运行结果: 

我们在本程序中是把两个没有内存重叠的两个数组进行的相互拷贝,但是在内存出现重叠的情况时,memcpy函数在某些编译器下是无法实现的,所以我们在下面引入memmove函数。


十三、memmove函数(内存拷贝函数)(内存重叠)

1.所需头文件

2. 参数类型

 我们看到参数类型与memcpy一致,在这里不重复介绍,参考memcpy即可

3.返回类型

 我们看到返回类型与memcpy一致,在这里不重复介绍,参考memcpy即可

4.memmove函数的使用方法及其注意事项

使用方法与memcpy函数一致

注意事项:

①.和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。

②.如果源空间和目标空间出现重叠,就得使用memmove函数处理。

5.模拟实现memmove函数

逐条语句解析: 

①.定义一个整型数组

②.把数组的地址传入到我们编写好的my_move函数,以及我们想要进行拷贝的元素大小,本程序我们想把1,2,3,4的数值拷贝到3,4,5,6的位置,所以需要拷贝的是4个整型元素,所以大小就是4*4=16(字节)

③.模拟memmove函数的参数以及返回类型的定义方法,由于这里不知道需要进行拷贝的元素类型,这里统一用void*来接收(void*类型可以接收任意类型的指针变量)

④.定义一个void*类型的指针变量用来存放目标字符串的地址,便于一会进行完拷贝操作后,返回拷贝后的目标字符串

⑤.以字节的大小去作为循环变量,我们用最细致的方法,一个字节一个字节的进行拷贝,这样保证可以每一种数据类型都可以拷贝过去

⑥.我们的拷贝有两种情况,第一种情况:当dest的地址小于src的地址,证明我们需要把src的内容向左拷贝,为了可以保证拷贝的成功,我们的src需要从小到大去进行拷贝

图解情况一:

 我们只有按照如图方式才可以把src的内容准确无误的拷贝到dest中

⑦.我们把dest和src分别强制类型转换成char*,这样每+1就跳过一个字节,这样的拷贝方式是最细致的方法,适用于每一种数据类型,每一次跳过一个字节,最后跳过总的count字节数即可完成拷贝操作

⑧.第二种情况当dest的地址大于src的地址,我们把前面的元素向后拷贝,所以是把元素向右拷贝,这时为了保证地址重叠部分不被先行改变,所以先拷贝最后一个元素,倒着进行赋值,这样可以保证拷贝的数据准确不出错误。

图解情况二:

 ⑨.因为我们情况二的拷贝需要倒着赋值,先赋值高地址的内容,所以我们先加上总字节数这样可以保证拷贝操作从最后一个元素进行,如上图的代码,我们需要进行拷贝的元素是4个整型元素,所以字节数是16个字节,因为进行了后置--操作,所以这时候的count已经是15,地址src和地址dest都是对应的拷贝位置的首地址,所以首地址+15对应的位置都是末元素的位置。

⑩.打印出来拷贝后的数组元素。

运行结果:


十四、memcmp函数(内存比较函数) 

1.所需头文件

2.参数类型 

两个需要进行的比较的字符串地址以及需要进行比较的元素个数的总大小(单位:字节)。

3.返回类型

我们看到memcmp的返回类型是int,所以就是同strcmp的返回方式是一致的,相等返回0,相异返回大于或者小于0的数字

4.memcmp的使用方法以及注意事项

#include <stdio.h>
#include <string.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[] = { 1,3,2,4,5,6,7,8,9,10 };
	int ret = memcmp(arr1, arr2, 16);
	printf("%d", ret);
}

 我们看到这里返回的数值是-1所以我们可以得到数组arr2是比数组arr1要大的,因为我们看到数组arr2[1]的元素要比arr1[1]的元素要大,这样终止了我们继续向后的比较,直接返回了一个小于0的数字。

注意事项:基本与strcmp函数相一致。

5.memcmp函数的模拟实现

 逐条语句解析: 

①~②.定义两个整型数组

③.传入两个数组的地址,以及需要进行比较的元素的总大小(单位:字节)

④.模拟memcmp函数的参数以及返回值的编写方法,定义两个void*指针存放需要进行比较的两个变量地址(这里由于不知道变量的类型,所以采用void*类型指针接收),以及一个无符号整型的count接收的需要进行比较的元素总大小(单位:字节)

⑤.利用总字节数作为循环的变量,循环完整个字节数即完成了所有元素的比较

⑥~⑦.以最细致的方法进行比较,把元素的地址类型转换为char*类型,char*类型每次可以访问一个字节,这样我们可以把元素进行逐一字节的比较

⑧.逐一比较每一个字节所对应的地址元素是不是相等,如果相等两个指针分别加1继续推移向下一个字节进行比较,如果两个元素存在大小的差异,直接返回这两个元素对应assic差异

图解代码:

 运行结果:


十五、memset函数(内存设置函数)

1.所需头文件

2.参数类型以及返回值

 

这里的返回类型是void*因为不知道元素的具体类型所以这里采用void*类型,返回的是设置变量完成后地址,第一个参数是目标元素,即我们需要进行内存设置操作的元素,第二个参数是设置的数据,第三个数需要进行的设置的元素总大小(单位:字节)

3. memset函数的使用方法

#include <stdio.h>
#include <string.h>
int main()
{
	int arr[10] = { 0 };
	memset(arr, 1, 16);
	return 0;
}

memset函数进行设置前的arr在内存的存储情况:

这里因为初始化的数组元素是0,所以这里的32个字节都是0

 这里进行了memset函数后把前16个字节都设置成了1

 4. memset函数的模拟实现

逐条代码解析: 

①.定义并且初始化数组

②.把数组的首地址传入到memset函数中,初始化的数据,以及需要进行设置的元素总大小(单位:字节)

③.模拟memset函数的参数类型和返回值类型的编写方法

④.判断dest地址的有效性

⑤.定义一个void*指针用来存放目标元素的地址方便一会返回其地址,由于不知道具体进行设置的元素类型,这里采用void*指针接收(void*指针可以接收任意类型的指针变量)

⑥.以总字节的大小作为循环的变量,循环完整个元素字节后就可以对整个元素都进行了设置

⑦~⑧.我们的机器都是小端存储的,所以我们对变量c进行一个强制类型的转换把变量c的地址强制类型转换为char*类型访问变量c的一个字节拿到的就是变量的有效位02,再把02初始化给目标元素,同样把目标元素也进行强制类型转换,把每一字节都进行初始化,每次向后+1直到循环了总字节的个数

⑨.返回设置完后的目标元素的地址

图解代码:

结果展示: 


总结

本篇文章就是对C语言中字符串及其库函数的详细解析,如果有存在问题,欢迎下面留言交流,谢谢大家的支持。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C语言----详解字符串相关的库函数(建议收藏) 的相关文章

  • NOKOV度量动捕软件教程(6):数据分析

    1 点击界面上方 窗口分割 2 个窗口 xff1a 上 下 选项 xff0c 让主界面分割为上下两个窗格 xff08 如图 xff09 xff0c 选中窗格时会有橙色边框 xff0c 此时点击 视图类型 xff0c 让其中一个窗口显示 3D
  • crazyswarm+crazyflie+NOKOV动捕飞控方案操作说明(3):通过VRPN协议对接

    一 crazyflie配置 1 查看 crazyflie 固件更新完毕后 xff0c 取下crazyflie xff0c 并连接电池 xff0c 把crazyradio接上电脑usb口 xff0c 运行以下命令 xff1a rosrun c
  • crazyswarm+crazyflie+NOKOV动捕飞控方案操作说明(4):SDK对接

    一 crazyswarm nokov 支持 1 更换libmotioncapture crazyswarm 支持nokov首先需要更换nokov专有libmotioncapture xff0c 进入路径 xff1a cd crazyswar
  • UE5与NOKOV度量动捕系统连接教程

    目录 一 动捕软件安装与数据准备 二 插件安装与 UE5 设置 一 xff09 插件安装 二 xff09 UE5 设置 xff08 实时播放模式 使用设备 xff1a 从仔动作捕捉套装 xff08 NOKOV度量 xff09 软件 xff1
  • Unity与NOKOV度量动捕系统连接教程

    目录 一 动捕软件安装与数据准备 二 插件安装与 Unity 设置 xff08 实时播放模式 xff09 三 插件安装与 Unity 设置 xff08 后处理下实时模式 xff09 使用设备 xff1a 从仔动作捕捉套装 xff08 NOK
  • MotionBuilder与NOKOV度量动捕系统连接教程

    目录 一 动捕软件安装 二 数据录制 导入与导出 xff08 一 xff09 创建 Markerset xff08 二 xff09 数据采集 xff08 三 xff09 数据导入 xff08 四 xff09 数据导出 三 插件安装与 Mot
  • NOKOV动作捕捉系统使多场协同无人机自主建造成为可能

    近年来 xff0c 工业机器人的兴起使得建造的效率和安全性得以提升 xff0c 但由于机器人由于大小与活动范围的限制 xff0c 在大型建筑上难以施展拳脚 上海同济大学建筑系的无人机自主建造小组 xff0c 正在进行以无人机取代工业机器人进
  • 多智能体系统集群协同控制实验平台详解与典型案例

    目录 一 机器人实验是智能体集群研究必要手段 二 动作捕捉系统解决智能体集群实验系统多个痛点 三 多智能体集群协同控制实验平台 1 Crazyswarm多无人机集群编队实验平台 2 Robotarium机器人平台 3 中科院自动化所智能集群
  • NOKOV度量动作捕捉协助完成无人机室内定位研究

    随着工业发展 技术进步 xff0c 无人机的使用在各行各业愈发普遍 xff0c 开始出现无人机飞行送外卖 智能无人机自主巡检等多方面应用 在这一过程中 xff0c 无人机飞行定位就成为了重中之重 西北工业大学无人机特种技术国防科技重点实验室
  • 光学动作捕捉系统构成

    一套光学动作捕捉系统由红外动作捕捉镜头 动作捕捉软件 反光标识点 POE交换机 和若干配件组成 xff08 如标定框和镜头固定装置等 xff09 其本质是定位系统 xff0c 通过计算分析 xff0c 来获取与其相关的速度 加速度等多种运动
  • vscode命令行起本地服务,可发送http请求

    在我们vscode中默认打开的是file协议 xff0c 但是往往我们会有ajax等请求 xff0c 需要发送http等其他协议 xff0c 所以我们需要搭起本地服务器 xff1a 1 xff1a cd 进去到文件位置 xff0c 运行 n
  • 动作捕捉用于仿生机器人的运动规划

    随着机器人 三维动画 虚拟现实等产业的发展 xff0c 关于仿生机器人的动作研究早已成为重要的热点课题 如何让机器人或虚拟人物做出合理 流畅的姿态呢 xff1f 这就要涉及到逆运动学算法研究 人体很复杂 xff0c 传统算法需优化 由于人体
  • 智能化人机协作 遮挡情况下准确识别目标信息

    研究背景 废旧产品 xff08 end of life products xff09 的拆卸是工程全生命周期管理的一个基本步骤 在减少资源消耗和温室气体排放的同时 xff0c 回收可重复使用的部件可能创造相当的经济价值 xff0c 同时也能
  • 线下·香港 | 工业大数据与智能系统前沿会议

    由香港理工大学主办的工业大数据与智能系统前沿会议将于2023年4月28日至5月1日在香港举行 届时来自海外 内地及香港的知名科学家将聚首 xff0c 将围绕大会主题 面向人机共融的工业转型 发表演讲 xff0c 分享他们的独到见解并探讨最新
  • 人机耦合模型研究及其在下肢外骨骼机器人设计中的应用

    在外骨骼研究中 xff0c 一个合适的人机耦合模型非常重要 xff0c 它可以帮助预测外骨骼系统直接作用于人体产生的影响 xff0c 避免不必要的伤害和能量损失 xff0c 同时也有助于优化外骨骼系统的设计和控制 xff0c 提高其佩戴的舒
  • STM32】 DMA原理,步骤超细详解,一文看懂DMA

    如需转载请注明地址 xff1a https blog csdn net as480133937 article details 104927922 DMA的基本介绍 什么是DMA DMA的基本定义 DMA xff0c 全称Direct Me
  • float类型数据在报文中的传输方法

    方法1 xff1a 转化成整型传输 假如保留float类型数据为两位小数 xff0c 我们可以将float数据 100 转换成整型数据传输 xff0c 对端收到后 xff0c 再 100 xff0c 转换成float类型 方法2 xff1a
  • 101、104规约解析

    转载 xff1a 电网101 104规约解析 xff08 Java xff09 张二狗和苗翠花的博客 CSDN博客 iec101 java 1 前言 最近在研究广东电网的101与104规约 xff0c 也就是DL T634 5101 200
  • Ubuntu:Python多版本切换。

    使用 update alternatives更改系统Python版本 1 查看可选版本 sudo update alternatives list python 如果提示 xff1a update alternatives error no
  • ROS(melodic)安装问题汇总及解决方法

    终于装上了ROS xff0c 费了很大的波折 xff0c 基本上能遇到的问题都遇到了 xff0c 记在这里希望能给遇到同样问题的朋友一点参考 首先是在虚拟机上装ubuntu 18 04 xff0c 这个没什么问题 xff0c 所用的镜像文件

随机推荐

  • Http请求出现invalid http response问题的原因分析

    发生场景 xff1a A系统发送Http请求调用B系统提供的接口 xff1b 发生问题 xff1a A系统报错 xff0c 提示 invalid http response 错误信息 xff1b 问题分析 xff1a 根据翻译 xff0c
  • STM32利用CUBEMX建立自定义HID工程,并且完成64字节的IN,OUT传输功能。

    STM32 Customed HID开发流程 本文介绍的是STM32的cubeMX自定义HID的开发流程 cubeMX配置customed HID模式 更多详细配置壳查看代码CubeMX的配置文件 修改usbd custome hid if
  • STM32 uart 单线半双工模式(cube版本)

    STM32 uart 单线半双工模式 xff08 cube版本 xff09 1 引言 在某些场合下需要进行三线制串口通信 xff08 信号线只有一根 xff09 xff0c 这就要求进行单线半双工的模式进行通信 在这种情况进行数据协议传输的
  • AS5600磁编码器开发记录

    AS5600使用简介 xff08 程序员版 xff09 本文由 智御电子 提供 xff0c 同时提供范例教程 xff0c 以便电子爱好者交流学习 前言 xff1a 最近由于工作需要接触到AS5600这颗磁角度传感器 xff0c 以前就对相关
  • STM32 硬件UART接收超时检测设置

    STM32 硬件UART接收超时检测设置 本文作者 智御电子 xff0c 期待与电子爱好者交流学习 应用场景 在uart应用中有时候需要进行双工通信 xff0c 主机需要对从机的数据进行接收超时检测 xff0c 例如modbus协议 xff
  • 发送GET请求 示例

    发送GET请求 64 param url 请求地址 64 param param 请求参数 64 param headers 64 return private String requestByGet String url Map lt S
  • STL不同容器的优缺点

    一 容器的分类 1 序列容器 xff08 1 xff09 vector 典型的序列容器 xff0c 任意元素的读取 修改具有O 1 xff0c 在序列尾部进行插入 删除是O 1 xff0c 但在序列的头部插入 删除的时间复杂度是O n xf
  • c语言(数组)

    交换算法 xff08 将最小值换到第一位 xff0c 最大值换到最后一位 xff09 include lt stdio h gt void main int o 61 0 int buf 10 接收用户输入的数组 for o lt 10 o
  • STM32外设串口资源用完了怎么办--------串口模拟解决问题(再也不用多个STM32或其它MCU)

    之前做项目的时候遇到了一个问题 xff0c 当把MCU本身的串口资源用完的时候 xff0c 却还需要使用多几个串口 xff0c 又不想使用几个MCU解决这个问题 那么模拟串口是解决这个问题的一种方法 下图是我对串口通信时序图的个人理解 xf
  • scanf函数中的格式字符串及注意事项

    scanf函数称为格式输入函数 xff0c 即按用户指定的格式从键盘上把数据输入到指定的变量之中 scanf函数的一般形式为 xff1a scanf 格式控制字符串 地址表列 xff1b 格式字符串的一般形式为 xff1a 输入数据宽度 长
  • 【C/C++】标准库, STL, Boost等的联系

    Backto C C 43 43 Index 标准库 最最开始 只有 C 语言 使用着使用着 一些常用的功能被写成了库 各种组织都是自己私有的库 后来为了方便统一使用和交流 就制定了标准 标准里的库 就是 C 标准库 后来 C 43 43
  • 数组与链表的优缺点和区别

    1 数组 xff1a 数组是将元素在内存中连续存放 xff0c 由于每个元素占用内存相同 xff0c 可以通过下标迅速访问数组中任何元素 但是如果要 在数组中增加一个元素 xff0c 需要移动大量元素 xff0c 在内存中空出一 个元素的空
  • 堆空间与栈空间的区别

    1 栈区 xff08 stack xff09 xff1a 又编译器自动分配释放 xff0c 存放函数的参数值 xff0c 局部变量的值等 xff0c 其操作方式类似于数据结构的 栈 2 堆区 xff08 heap xff09 xff1a 一
  • strtok函数及其实现

    头文件 xff1a include lt string h gt 定义函数 xff1a char strtok char s const char delim 函数说明 xff1a strtok 用来将字符串分割成一个个片段 参数s 指向欲
  • 服务器和客户机的信息函数以及读写函数

    1 服务器和客户机的信息函数 xff08 1 xff09 字节转换函数 在网络上面有着许多类型的机器 xff0c 这些机器在表示数据的字节顺序是不同的 xff0c 比如i386芯片是低字节在内存地址的低 端 xff0c 高字节在高端 xff
  • TCP协议的拥塞控制机制

    最初的TCP协议只有基于窗口的流控制 xff08 flow control xff09 机制而没有拥塞控制机制 流控制作为接受方管理发送方发送 数据的方式 xff0c 用来防止接受方可用的数据缓存空间的溢出 流控制是一种局部控制机制 xff
  • 构造函数为什么不能是虚函数

    1 从存储空间角度 xff0c 虚函数对应一个指向vtable虚函数表的指针 xff0c 这大家都知道 xff0c 可是这个指向vtable的指针其实是存储在对象的内存空间的 问题出来了 xff0c 如果构造函数是虚的 xff0c 就需要通
  • openmv学习五:OLED

    首先需要将SSD1306x py这个文件放到OPENMV中 代码 from machine import I2C Pin 从 machine 模块导入 I2C Pin 子模块 from ssd1306x import SSD1306 I2C
  • 自旋锁的实现及优化

    自旋锁也是一种互斥锁 xff0c 和mutex锁相比 xff0c 它的特点是不会阻塞 xff0c 如果申请不到锁 xff0c 就会不断地循环检测锁变量的状态 xff0c 也就是自旋 自旋锁的实现算法大多使用TAS算法或者CAS算法 TAS算
  • C语言----详解字符串相关的库函数(建议收藏)

    文章目录 系列文章目录前言一 C语言相关字符串库函数一览表 二 strlen函数 三 strcpy函数四 strcat函数 五 strcmp函数 六 strncpy函数 七 strncat函数 八 strncmp函数 九 strstr函数