文章目录
- 系列文章目录
- 前言
- 一、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语言中字符串及其库函数的详细解析,如果有存在问题,欢迎下面留言交流,谢谢大家的支持。
|