测试环境基于VS2022版本
一、strcpy
1.函数介绍:
strcpy用于将源字符数组中的内容(包括终止符 \0
)按字节顺序复制到目标字符数组中
函数原型:
char *strcpy( char *strDestination, const char *strSource );
- strDestination 指向目标数组的指针,用于存放复制后的字符串
- strSource 指向要复制的源字符串的指针(不可修改)
- 返回值:目标数组起始地址(即strDestination的值)
2.函数的使用
strcpy的使用需要引入头文件<string.h>
接下来分别创建两个数组,将一个数组中的内容拷贝到另一个数组中去
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = "Hello";
char arr2[20] = "Good night";
printf("拷贝前:%s\n", arr1);
strcpy(arr1, arr2);
printf("拷贝后:%s\n", arr1);
return 0;
}
执行代码后,输出内容如下:
拷贝前:Hello
拷贝后:Good night
可以发现,strcpy复制到目标数组是直接覆盖,那么接下来我们模拟实现以下库函数strcpy
3.strcpy的模拟实现
函数原型:
char *strcpy( char *strDestination, const char *strSource );
观察函数原型 ,函数的两个参数是目标空间与源空间,又通过之前的介绍可知,返回类型为目标空间的起始地址。
要实现字符的复制,可以通过找到两块空间的地址,然后解引用,把需要拷贝的字符放入目标空间中,接着两块指针地址+1,重复上述步骤,当遇到字符串的结束标识后便停止复制
有了以上内容之后,想要模拟实现就异常简单了。
首先定义函数:
char* my_strcpy(char* dest,const char* sorce);
接下来实现字符的拷贝操作:
*dest = *sorce;
dest++;
sorce++;
遇到标识符就停止复制,使用while循环,遍历数组,当遇到'\0\时候就跳出循环
最后,返回目标空间的起始位置,那么在函数开头部分定义一个变量存储该位置,最后返回该变量的值就可以解决。
这样,strcpy函数我们就可以模拟出来了:
char* my_strcpy(char* dest, const char* sorce)
{
char* ret = dest;
while (*sorce != '\0')
{
*dest = *sorce;
dest++;
sorce++;
}
return ret;
}
函数验证:
int main()
{
char arr1[20] = "Hello";
char arr2[20] = "Good Night";
printf("复制前:arr1 = %s\n", arr1);
char* p1 = my_strcpy(arr1, arr2);
printf("复制后:arr1 = %s\n", arr1);
printf("返回p1 = %s\n", p1);
return 0;
}
执行代码后,输出内容如下:
复制前:arr1 = Hello
复制后:arr1 = Good Night
返回p1 = Good Night
结果与strcpy函数的输出内容一致,成功实现strcpy的模拟。
strcpy与strnpy核心原理一致,区别在于strcpy是全部内容均复制,strnpy则是代表复制n个字符
二、strnpy
1.函数介绍:
strncpy用于将源字符数组中的内容(包括终止符 \0
)按字节顺序复制到目标字符数组中
函数原型:
char * strncpy ( char * destination, const char * source, size_t num );
- strDestination 指向目标数组的指针,用于存放复制后的字符串
- strSource 指向要复制的源字符串的指针(不可修改)
- num 需要复制的元素个数
- 返回值:目标数组起始地址(即strDestination的值)
2.函数的使用
strncpy的使用需要引入头文件<string.h>
接下来分别创建两个数组,将一个数组中的内容拷贝到另一个数组中去
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = "abcdefghi";
char arr2[20] = "1234567";
printf("拷贝前:%s\n", arr1);
strncpy(arr1, arr2,5);
printf("拷贝后:%s\n", arr1);
return 0;
}
执行代码后,输出内容如下:
拷贝前:abcdefghi
拷贝后:12345fghi
3.strncpy的模拟实现
函数原型:
char * strncpy ( char * destination, const char * source, size_t num );
观察函数原型 ,函数的两个参数是目标空间与源空间,又通过之前的介绍可知,返回类型为目标空间的起始地址。
要实现字符的复制,可以通过找到两块空间的地址,然后解引用,把需要拷贝的字符放入目标空间中,接着两块指针地址+1,重复上述步骤,当遇到字符串的结束标识后便停止复制
有了以上内容之后,想要模拟实现就异常简单了。
首先定义函数:
char * my_strncpy ( char * destination, const char * source, size_t num );
接下来实现字符的拷贝操作:
*dest = *sorce;
dest++;
sorce++;
使用循环结构拷贝字符,每处理一个字符就将计数器num减1,直至num归零时退出循环。最终返回数组的首地址。
char* my_strncpy(char* dest, const char* sorce,size_t num)
{
char* ret = dest;
while (num--)
{
*dest = *sorce;
dest++;
sorce++;
}
return ret;
}
函数验证:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
char* my_strncpy(char* dest, const char* sorce,size_t num)
{
char* ret = dest;
while (num--)
{
*dest = *sorce;
dest++;
sorce++;
}
return ret;
}
int main()
{
char arr1[20] = "abcdefghi";
char arr2[20] = "1234567";
printf("拷贝前:%s\n", arr1);
my_strncpy(arr1, arr2,5);
printf("拷贝后:%s\n", arr1);
return 0;
}
执行代码后,输出内容如下:
拷贝前:abcdefghi
拷贝后:12345fghi
结果与strncpy函数的输出内容一致
三、strcmp
1.函数介绍:
strcmp是用于两个字符串的对比
函数原型:
int strcmp( const char *string1, const char *string2 );
- string1 指向待比较的第一个字符串(以空字符终止的字符数组)
- string2 指向待比较的第二个字符串(以空字符终止的字符数组)
- 比较规则:
- 逐字符比较两个字符串对应位置上的字符(按ASCII码值),直至遇到不相等的字符或字符串结束符'\0'。
- 返回值:
- 若string1 > string2 返回1
- 若string1 < string2 返回-1
- 若string1 = string2 返回0
2.函数的使用
strcmp的使用需要引入头文件<string.h>
接下来分别创建两个字符数组,用于字符的比较。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
void strcmp_ret(int ret,char* str1,char* str2)
{
if (ret > 0)
printf("%s > %s\n",str1,str2);
else if (ret == 0)
printf("%s == %s\n",str1,str2);
else
printf("%s < %s\n",str1,str2);
}
int main()
{
char arr1_1[10] = "abcde";
char arr1_2[10] = "abcd";
char arr2_1[10] = "abcde";
char arr2_2[10] = "abcdef";
char arr3_1[10] = "abcde";
char arr3_2[10] = "abcde";
char arr4_1[10] = "abcde";
char arr4_2[10] = "abcdddddd";
int arr1 = strcmp(arr1_1, arr1_2);
int arr2 = strcmp(arr2_1, arr2_2);
int arr3 = strcmp(arr3_1, arr3_2);
int arr4 = strcmp(arr4_1, arr4_2);
strcmp_ret(arr1, arr1_1, arr1_2);
strcmp_ret(arr2, arr2_1, arr2_2);
strcmp_ret(arr3, arr3_1, arr3_2);
strcmp_ret(arr4, arr4_1, arr4_2);
return 0;
}
执行代码后,输出内容如下:
"abcde" > "abcd"
"abcde" < "abcdef"
"abcde" == "abcde"
"abcde" > "abcdddddd"
结果剖析:
1. "abcde" > "abcd" → 第五位 'e'> 空字符
2. "abcde" < "abcdef" → 第六位 空字符 < 'f'
3. "abcde" == "abcde" → 全等匹配直至终止符
4. "abcde" > "abcdddddd" → 第五位 'e' > 'd'
可见,strcmp的比较机制为:从首字符开始逐个比较ASCII值:
1. 当遇到两个字符串的首个不匹配字符时,返回基于该字符比较的结果
2. 若扫描中某个字符串遇到终止符'\0'而另一个未结束,则认定前者较小
3. 所有字符匹配且结束条件同步时返回0
3.strcmp的模拟实现
函数原型:
int strcmp( const char *string1, const char *string2 );
观察函数原型 ,函数的两个参数是需要进行对比的两个字符数组。
前面已经提到,strcmp的比较机制,那么想要模拟实现就尤其简单了
首先定义函数:
int my_strcmp(const char* str1, const char* str2);
从两个字符串的起始位置开始同步扫描
每次提取单个字符进行ASCII值比较
关键循环逻辑
while (*str1 != '\0' && *str2 != '\0' (*str1 == *str2))
{
str1++;
str2++;
}
当且仅当遇到以下情况 则循环结束:
1. str1和str2当前字符不相等
2. str1或str2遇到'\0'
最后,比较当前str1与str2的元素
这样,strcmp函数我们就可以模拟出来了
int my_strcmp(const char* str1, const char* str2)
{
while (*str1 != '\0' && *str2 != '\0' && *str1 == *str2)
{
str1++;
str2++;
}
if (*str1 > *str2)
return 1;
else if (*str1 < *str2)
return -1;
return 0;
}
函数验证:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
void strcmp_ret(int ret,char* str1,char* str2)
{
if (ret > 0)
printf("%s > %s\n",str1,str2);
else if (ret == 0)
printf("%s == %s\n",str1,str2);
else
printf("%s < %s\n",str1,str2);
}
int my_strcmp(const char* str1, const char* str2)
{
while (*str1 != '\0' && *str2 != '\0' && *str1 == *str2)
{
str1++;
str2++;
}
if (*str1 > *str2)
return 1;
else if (*str1 < *str2)
return -1;
return 0;
}
int main()
{
char arr1_1[10] = "abcde";
char arr1_2[10] = "abcd";
char arr2_1[10] = "abcde";
char arr2_2[10] = "abcdef";
char arr3_1[10] = "abcde";
char arr3_2[10] = "abcde";
char arr4_1[10] = "abcde";
char arr4_2[10] = "abcdddddd";
int arr1 = my_strcmp(arr1_1, arr1_2);
int arr2 = my_strcmp(arr2_1, arr2_2);
int arr3 = my_strcmp(arr3_1, arr3_2);
int arr4 = my_strcmp(arr4_1, arr4_2);
strcmp_ret(arr1, arr1_1, arr1_2);
strcmp_ret(arr2, arr2_1, arr2_2);
strcmp_ret(arr3, arr3_1, arr3_2);
strcmp_ret(arr4, arr4_1, arr4_2);
return 0;
}
执行代码后,输出内容如下:
abcde > abcd
abcde < abcdef
abcde == abcde
abcde > abcdddddd
结果与strcmp函数的输出内容一致,成功实现strcmp的模拟。
四、strncmp
1.函数介绍:
strncmp是用于两个字符串的对比(指定比对个数)
函数原型:
int strncmp ( const char * str1, const char * str2, size_t num );
- string1 指向待比较的第一个字符串(以空字符终止的字符数组)
- string2 指向待比较的第二个字符串(以空字符终止的字符数组)
- num 要比较的最大元素个数。
- 比较规则:
- 逐字符比较两个字符串对应位置上的字符(按ASCII码值),直至遇到不相等的字符或字符串结束符'\0',或者达到比对元素个数。
- 返回值:
- 若string1 > string2 返回 1
- 若string1 < string2 返回 -1
- 若string1 = string2 返回 0
2.函数的使用
strncmp的使用需要引入头文件<string.h>
接下来分别创建两个字符数组,用于字符的比较。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char str1[10] = "abcde";
char str2[10] = "abcd";
int ret = strncmp(str1, str2,4);
if (ret > 0)
printf("%s > %s\n", str1, str2);
else if (ret == 0)
printf("%s == %s\n", str1, str2);
else
printf("%s < %s\n", str1, str2);
return 0;
}
执行代码后,输出内容如下:
abcde == abcd
结果剖析:
abcde == abcd -->设置的最大带比较元素个数为4,最多比对到第4个结束对比
可见,strcnmp的比较机制为与strncmp一致,只是strncmp添加了一个最大比对元素个数的限制条件。
3.strncmp的模拟实现
函数原型:
int strncmp(const char* str1, const char* str2, size_t num);
观察函数原型时,该函数的两个参数是需要比较的两个字符数组。
前文已介绍过strncmp的比较机制,因此模拟实现起来就变得非常简单。
首先定义函数:
int my_strncmp(const char* str1, const char* str2, size_t num);
从两个字符串的起始位置开始同步扫描
每次提取单个字符进行ASCII值比较
if (*str1 > *str2)
return 1;
else if (*str1 < *str2)
return -1;
比较后若未返回结果,说明当前两元素相等,此时将str1和str2的指针各后移一位。但移动前需检查指针是否到达数组末尾,避免越界访问。若任一指针已到末尾,则不再移动,表明该数组的所有元素已比对完毕。在未达到最大比较次数num的情况下,另一数组必然更大。
if (*str1 != '\0' && *str2 != '\0' )
{
str1++;
str2++;
}
最后,当比较次数达到最大值num时退出循环。若此时仍未发现str1和str2的不同元素,则返回0,表示这两个字符数组的前num个元素完全相同。
int my_strncmp(const char* str1, const char* str2, size_t num)
{
while (num--)
{
if (*str1 > *str2)
{
return 1;
}
else if (*str1 < *str2)
{
return -1;
}
if (*str1 != '\0' && *str2 != '\0' )
{
str1++;
str2++;
}
}
return 0;
}
函数验证:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int my_strncmp(const char* str1, const char* str2, size_t num)
{
while (num--)
{
if (*str1 > *str2)
{
return 1;
}
else if (*str1 < *str2)
{
return -1;
}
if (*str1 != '\0' && *str2 != '\0' )
{
str1++;
str2++;
}
}
return 0;
}
void strncmp_ret(int ret, char* str1, char* str2)
{
if (ret > 0)
printf("%s > %s\n", str1, str2);
else if (ret == 0)
printf("%s == %s\n", str1, str2);
else
printf("%s < %s\n", str1, str2);
}
int main()
{
char str1[10] = "abcde";
char str2[10] = "abcd";
int ret1 = my_strncmp(str1, str2, 4);
int ret2 = my_strncmp(str1, str2, 7);
strncmp_ret(ret1, str1, str2);
strncmp_ret(ret2, str1, str2);
return 0;
}
执行代码后,输出内容如下:
abcde == abcd
abcde > abc
结果与strncmp函数的输出内容一致,成功实现strncmp的模拟。
五、strlen
1.函数介绍:
strlen通常用于计算字符串的长度
函数原型:
size_t strlen ( const char * str );
- str 指向需计算长度的字符串
- 计算规则:
- 逐字符计算字符串字符个数,直至遇到字符串结束符'\0'。
- 返回值:
- 字符数组中元素个数
2.函数的使用
strlen的使用需要引入头文件<string.h>
接下来创建一个字符数组,尝试求出他的长度。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char str1[20] = "abcdef";
char str2[20] = "abc def";
size_t ret1 = strlen(str1);
size_t ret2 = strlen(str2);
printf("ret1 = %zd\n", ret1);
printf("ret2 = %zd\n", ret2);
return 0;
}
执行代码后,输出内容如下:
ret1 = 6
ret2 = 7
结果剖析:
1. ret1 = 6 → 第6位 之后为 终止符
2. ret2 = 7 → 第7位 之后为终止符
可见,strlen的计算机制为:从首字符开始,遇到一个字符,计数器就+1,直到遇见终止符'\0'
3.strlen的模拟实现
函数原型:
size_t strlen ( const char * str );
观察函数原型 ,函数的参数为待计算长度的字符串
前面已经提到,strlen的计算机制,那么想要模拟实现就尤其简单了
首先定义函数:
size_t my_strlen ( const char * str );
核心逻辑为从首元素开始遍历数组,直到遇到终止符才停下
while (*str != '\0')
{
str++;
}
那么再定义一个计数器,当指针向后移动一位,计数器就+1;
size_t count = 0;
while (*str != '\0')
{
count++;
str++;
}
最后,返回计数器的值
size_t my_strlen(const char* str)
{
size_t count = 0;
while (*str != '\0')
{
count++;
str++;
}
return count;
}
函数验证:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
size_t my_strlen(const char* str)
{
size_t count = 0;
while (*str != '\0')
{
count++;
str++;
}
return count;
}
int main()
{
char str1[20] = "abcdef";
char str2[20] = "abc def";
size_t ret1 = my_strlen(str1);
size_t ret2 = my_strlen(str2);
printf("ret1 = %zd\n", ret1);
printf("ret2 = %zd\n", ret2);
return 0;
}
执行代码后,输出内容如下:
ret1 = 6
ret2 = 7
结果与strlen函数的输出内容一致,成功实现strlen的模拟。
六、strstr
1.函数介绍:
strstr 用于字符串的子串查找
函数原型:
char* strstr(const char* str1, const char* str2)
- str1 指向被查找的母串
- str2 指向需要查找的内容(子串)
- 查找规则:
- 逐字符对比,遇到结束标识符或出现不同字符时退出比对。
- 返回值:
- 若找到,返回子串首元素在母串中出现的位置
- 若未找到,返回NULL
2.函数的使用
strstr的使用需要引入头文件<string.h>
接下来分别创建一个母串和子串
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char strA_1[30] = "This is a simple string";
char strA_2[30] = "simple";
char strB_1[30] = "abccba";
char strB_2[30] = "cba";
char strC_1[30] = "This is a simple string";
char strC_2[30] = "NO";
char* A = strstr(strA_1, strA_2);
char* B = strstr(strB_1, strB_2);
char* C = strstr(strC_1, strC_2);
printf("A = %s\n",A);
printf("B = %s\n",B);
printf("C = %s\n",C);
printf("&strA_1 = %p\n", strA_1);
printf(" &A = %p\n", A);
printf("&strB_1 = %p\n", strB_1);
printf(" &B = %p\n", B);
printf("&strC_1 = %p\n", strC_1);
printf(" &C = %p\n", C);
return 0;
}
执行代码后,输出内容如下:
A = simple string
B = cba
C = (null)
&strA_1 = 0000003882EFFB48
&A = 0000003882EFFB52
&strB_1 = 0000003882EFFBC8
&B = 0000003882EFFBCB
&strC_1 = 0000003882EFFC48
&C = 0000000000000000
结果剖析:
案例A:
- 输出字符串: "simple string" (表示在母串中找到子串,并返回从子串首次出现位置开始的字符串)
- 母串首地址: 0000003882EFFB48
- 子串首地址: 0000003882EFFB52 (母串中找到子串的起始位置)
案例B:
- 输出字符串: "cba" (找到子串)
- 母串首地址: 0000003882EFFBC8
- 子串首地址: 0000003882EFFBCB (母串中找到子串的起始位置)
案例C:
- 输出: (null) (表示未找到子串)
- 母串首地址: 0000003882EFFC48
- 返回地址: 0000000000000000 (空指针,表示未找到)
3.strlen的模拟实现
函数原型:
char* my_strstr(const char* str1, const char* str2)
观察函数原型 ,函数的参数为被查询的字符串和要查询的字符串
首先定义函数:
char* my_strstr(const char* str1, const char* str2)
核心逻辑为从首元素开始遍历数组,直到遇到不相同的元素或终止符才停下
while (*str1 != '\0' && *str2 != '\0' && *str1 == *str2)
{
str1++;
str2++;
}
需要注意的是,上述代码存在一个问题:当需要查找母串中间的元素时,可能无法进入循环。解决方法是将母串当前元素与子串首元素进行对比,若不相同,则将指针移向母串的下一个元素,继续与子串首元素进行比对,直到找到匹配项或遇到结束符为止。
while (*str1 != '\0' && *str2 != '\0' && *str1 != *str2)
{
str1++;
}
while (*str1 != '\0' && *str2 != '\0' && *str1 == *str2)
{
str1++;
str2++;
}
当母串为"abcabbb"、子串为"abb"时,按照上述方法进行匹配(如加粗部分所示),可能会出现误判母串中不存在子串的情况。如何解决这个问题?
解决方案如下:
- 创建一个临时指针tmp,初始指向母串的首字符
- 采用前述匹配方法进行比对:
- 若首次匹配未找到子串,则tmp指针后移一位
- 重复上述过程,直至出现以下情况之一: a) tmp指向终止符'\0',说明母串中确实不存在子串,返回NULL b) 子串指针到达终止符前所有字符均匹配成功,说明找到子串,返回当前tmp指针
如图所示,直到s2遇到结束标志或者tmp遇到结束标志,就返回值
- s2遇到结束标志代表母串中包含子串,返回子串首次出现位置开始的字符串
- tmp遇到结束标识代表直到最后一刻都没有找到子串, 返回NULL
char* my_strstr(const char* str1, const char* str2)
{
const char* s1 = NULL;
const char* s2 = NULL;
const char* tmp = str1;
while (*tmp)
{
s1 = tmp;
s2 = str2;
while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return tmp;
}
tmp++;
}
return NULL;
}
函数验证:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
char* my_strstr(const char* str1, const char* str2)
{
const char* s1 = NULL;
const char* s2 = NULL;
const char* tmp = str1;
while (*tmp)
{
s1 = tmp;
s2 = str2;
while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return tmp;
}
tmp++;
}
return NULL;
}
int main()
{
char strA_1[30] = "This is a simple string";
char strA_2[30] = "simple";
char strB_1[30] = "abccba";
char strB_2[30] = "cba";
char strC_1[30] = "This is a simple string";
char strC_2[30] = "NO";
char* A = my_strstr(strA_1, strA_2);
char* B = my_strstr(strB_1, strB_2);
char* C = my_strstr(strC_1, strC_2);
printf("A = %s\n",A);
printf("B = %s\n",B);
printf("C = %s\n",C);
printf("&strA_1 = %p\n", strA_1);
printf(" &A = %p\n", A);
printf("&strB_1 = %p\n", strB_1);
printf(" &B = %p\n", B);
printf("&strC_1 = %p\n", strC_1);
printf(" &C = %p\n", C);
return 0;
}
执行代码后,输出内容如下:
A = simple string
B = cba
C = (null)
&strA_1 = 00000090035BF578
&A = 00000090035BF582
&strB_1 = 00000090035BF5F8
&B = 00000090035BF5FB
&strC_1 = 00000090035BF678
&C = 0000000000000000
结果与strstr函数的输出内容一致,成功实现strstr的模拟。
七、memcpy
1.函数介绍:
memcpy是完成内存块的拷贝,不关心具体内容是什么
函数原型:
void * memcpy ( void * destination, const void * source, size_t num );
- destination 指向目标空间,拷贝后的内容存放在该空间中
- source 指向源空间,需要拷贝的内容从该空间中拿取
- num 拷贝的数据大小(以字节为单位)(如4个int类型,就需填值为20)
- 复制规则:
- 逐字节拷贝内容,直至达到设置的字节数。
2.函数的使用
memcpy的使用需要引入头文件<string.h>
接下来创建两个数组,尝试将其中一个数组的内容拷贝到另一个数组中去。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[10] = { 1,2,3,4,5 };
int arr2[10] = { 5,6,7,8,9 };
memcpy(arr1, arr2, sizeof(int) * 4);
int i = 0;
for ( i = 0; i < 5; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
执行代码后,输出内容如下:
5 6 7 8 5
结果剖析:
5 6 7 8 5 填入拷贝字节数为20,在arr2中为前4个元素,将前4个元素拷贝(覆盖)到arr1中
3.memcpy的模拟实现
函数原型:
void * memcpy ( void * destination, const void * source, size_t num );
函数原型的几个参数前面已经介绍,这里不再赘述
首先定义函数:
void * my_memcpy ( void * destination, const void * source, size_t num );
核心逻辑为从首元素开始以字节为单位进行拷贝,直到达到设置的拷贝数据大小。
while (num--)
{
*(char*)destination = *(char*)source;
(char*)destination += 1;
(char*)source += 1;
}
最后,以防目标空间与源空间指针为NULL,添加告警(assert)(需引入头文件<assert.h>)
void* my_memcpy(void* destination, const void* source, size_t num)
{
assert(destination);
assert(source);
while (num--)
{
*(char*)destination = *(char*)source;
(char*)destination += 1;
(char*)source += 1;
}
}
函数验证:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <assert.h>
void* my_memcpy(void* destination, const void* source, size_t num)
{
assert(destination);
assert(source);
while (num--)
{
*(char*)destination = *(char*)source;
(char*)destination += 1;
(char*)source += 1;
}
}
int main()
{
int arr1[10] = { 1,2,3,4,5 };
int arr2[10] = { 5,6,7,8,9 };
my_memcpy(arr1, arr2, sizeof(int) * 4);
int i = 0;
for ( i = 0; i < 5; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
执行代码后,输出内容如下:
5 6 7 8 5
结果一致
八、memcmp
1.函数介绍:
memcmp的左右是对比str1与str2指向的内容
函数原型:
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
- ptr1 指向待比较的第一块内存空间
- ptr2 指向待比较的另一块内存空间
- num 需要比较的数据大小(以字节为单位)(如4个int类型,就需填值为20)
- 比对规则:
- 逐字节比对内容,直到出现不同或达到设置的字节数。
- 返回值:
- 若ptr1 > ptr2 返回 1
- 若ptr1 < ptr2 返回 -1
- 若ptr1 = ptr2 返回 0
2.函数的使用
memcpy的使用需要引入头文件<string.h>
接下来创建两个数组,尝试比较两数组的大小关系。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char str1[20] = "S&hd8*/+=ds";
char str2[20] = "S&hd8*/+=ds";
int ret = memcmp(str1, str2, sizeof(char) * 11);
if (ret > 0)
{
printf("%s > %s\n", str1, str2);
}
else if (ret == 0)
{
printf("%s == %s\n", str1, str2);
}
else
{
printf("%s < %s\n", str1, str2);
}
return 0;
}
执行代码后,输出内容如下:
S&hd8*/+=ds == S&hd8*/+=ds
结果剖析:
S&hd8*/+=ds == S&hd8*/+=ds 逐字节对比,对比11个字节后没有找到不同,则输出==
3.memcpy的模拟实现
函数原型:
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
函数原型的几个参数前面已经介绍,这里不再赘述
首先定义函数:
void * my_memcpy ( void * destination, const void * source, size_t num );
从两个字符串的起始位置开始同步扫描
每次提取单个字符进行ASCII值比较
由于函数参数是void*类型,在使用前需要先进行逐字节的类型转换。void*指针虽然可以指向任何数据类型,但由于它本身没有类型信息,编译器无法知道它指向的具体数据类型,因此无法直接进行指针运算或解引用操作。
考虑到memcpy是通过逐字节操作实现的,将参数转换为char*类型是最合适的选择。
if (*(char*)ptr1 > *(char*)ptr2)
{
return 1;
}
else if(*(char*)ptr1 < *(char*)ptr2)
{
return -1;
}
比较后若未返回结果,说明当前两元素相等,此时将str1和str2的指针各后移一位。但移动前需检查指针是否到达数组末尾,避免越界访问。若任一指针已到末尾,则不再移动,表明该数组的所有元素已比对完毕。在未达到最大比较次数num的情况下,另一数组必然更大。
if (*(char*)ptr1 == *(char*)ptr2 && *(char*)ptr1 != '\0' && *(char*)ptr2 != '\0')
{
(char*)ptr1 += 1;
(char*)ptr2 += 1;
}
然后,函数参数中设置的最大比较数是num个字节,所以再将上述代码放到一个循环中,让其最多执行num次
while (num--)
{
if (*(char*)ptr1 > *(char*)ptr2)
{
return 1;
}
else if(*(char*)ptr1 < *(char*)ptr2)
{
return -1;
}
if (*(char*)ptr1 == *(char*)ptr2 && *(char*)ptr1 != '\0' && *(char*)ptr2 != '\0')
{
(char*)ptr1 += 1;
(char*)ptr2 += 1;
}
}
最后,以防ptr1与ptr2指向的空间为空(NULL),添加告警(assert)(需引入头文件<assert.h>)以避免程序崩溃。
#include <assert.h>
assert(ptr1);
assert(ptr2);
模拟memcpy函数:
int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
assert(ptr1);
assert(ptr2);
while (num--)
{
if (*(char*)ptr1 > *(char*)ptr2)
{
return 1;
}
else if(*(char*)ptr1 < *(char*)ptr2)
{
return -1;
}
if (*(char*)ptr1 == *(char*)ptr2 && *(char*)ptr1 != '\0' && *(char*)ptr2 != '\0')
{
(char*)ptr1 += 1;
(char*)ptr2 += 1;
}
}
return 0;
}
函数验证:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <assert.h>
int my_memcmp(const void* ptr1, const void* ptr2, size_t num)
{
assert(ptr1);
assert(ptr2);
while (num--)
{
if (*(char*)ptr1 > *(char*)ptr2)
{
return 1;
}
else if(*(char*)ptr1 < *(char*)ptr2)
{
return -1;
}
if (*(char*)ptr1 == *(char*)ptr2 && *(char*)ptr1 != '\0' && *(char*)ptr2 != '\0')
{
(char*)ptr1 += 1;
(char*)ptr2 += 1;
}
}
return 0;
}
int main()
{
char str1[20] = "S&hd8*/+=ds";
char str2[20] = "S&hd8*/+=ds";
int ret = memcmp(str1, str2, sizeof(char) * 11);
if (ret > 0)
{
printf("%s > %s\n", str1, str2);
}
else if (ret == 0)
{
printf("%s == %s\n", str1, str2);
}
else
{
printf("%s < %s\n", str1, str2);
}
return 0;
}
执行代码后,输出内容如下:
S&hd8*/+=ds == S&hd8*/+=ds
结果一致
end
测试环境基于VS2022版本