作者:热爱编程的小y
专栏:C语言基础
座右铭:岁月漫长,然而值得等待
![](https://img-blog.csdnimg.cn/img_convert/04bd82c7da7bad9139cd2c09c33ad8cc.jpeg)
Strcpy
strcpy的功能
strcpy,即string copy(字符串复制)的缩写。
strcpy是一种C语言的 标准库函数,strcpy把从src地址开始且含有’\0’结束符的字符串复制到以dest开始的地址空间,返回值的类型为char*。
库函数中的strcpy
使用前需包含头文件<string.h>
是众多字符串函数中的一种
cplusplus中对strcpy的解释:
![](https://img-blog.csdnimg.cn/img_convert/65f24ea4afe0b3ec6c982e732faabbe0.png)
函数包含两个参数,即复制对象destination与复制内容source,返回值是一个char类型的指针
![](https://img-blog.csdnimg.cn/img_convert/6be526abd53f18f1bc92c1a2278c3090.png)
strcpy的具体运用
目的地:dest[20]="\0";
源:src[]="hello world"
我们要把源中的"hello world"复制到目的地dest中,只需要使用函数strcpy(dest,src)
#include<string.h>
int main()
{
char dest[20] = "\0";
char src[] = "hello world";
strcpy(dest, src);
return 0;
}
![](https://img-blog.csdnimg.cn/img_convert/cbc932e7adabfca173bd4df4b8bbf8bc.png)
此时dest内部前11项已经从全是"\0"变成了"hello world"
注意
目的地dest[]必须足够大即"[]"内的数字必须不小于src[]的元素个数,否则就会出现数组越界的情况
![](https://img-blog.csdnimg.cn/img_convert/a94f1383cb534cd27681c4f0c1a2121f.png)
strcpy的模拟实现
#include<assert.h>
char* my_strcpy(char* dest, const char*str)
{
assert(dest && str);//断言,确保指针dest,str有效
char* ret = dest;//定义返回值 why?:因为后续操作会改动dest的初始地址,不能直接用于返回
while (*dest++ = *str++)
{
;
}
return ret;
}
Memcpy
memcpy的功能
memcpy是memory copy的缩写,意为内存复制,它的功能是从src的开始位置拷贝n个字节的数据到dest。如果dest存在数据,将会被覆盖。memcpy函数的返回值是dest的指针。memcpy函数定义在string.h头文件里。
与strcpy的不同
1.内容不同
strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
2.方法不同
strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
总而言之,strcpy能干的memcpy也能干,memcpy能干的strcpy则不一定能干,由此可见memcpy的强大之处
但是memcpy并不是完美的,碰到一些特殊情况会达不到预期结果,在这里先埋个伏笔,下面讲到memmove的时候会进行解答
库函数中的memcpy
![](https://img-blog.csdnimg.cn/img_convert/7cfca7f3f4cf2e42fc9b2ff1d2eae304.png)
memcpy函数包含三个参数(复制对象,复制内容,复制的长度),返回值为无类型指针
cplusplus中对memcpy的解释:
![](https://img-blog.csdnimg.cn/img_convert/31d77d83ee8df896a0117eeb8b50211a.png)
memcpy的具体运用
typedef struct S
{
char name[10];
int age;
}S;
int main()
{
S src[] = { {"Zhang San",20},{"Lee Si",30} };
S dest[3] = { 0 };
memcpy(dest, src, sizeof(src));
return 0;
}
![](https://img-blog.csdnimg.cn/img_convert/05a4edfbf0cc261e430d2651f438d01f.png)
注意
同上,目的地dest[]必须足够大,否则越界
memcpy的模拟实现
#include<assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{
assert(dest && src);//断言,确保指针有效
void* ret = dest;//定义返回值
while (num--)
{
*(char*)dest = *(char*)src;
++(char*)dest;
++(char*)src;
}
return ret;
}
过渡
那么memcpy到底在什么情况下会出现问题呢?请看以下代码:
#include<assert.h>
void* my_memcpy(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
while (num--)
{
*(char*)dest = *(char*)src;
++(char*)dest;
++(char*)src;
}
return ret;
}
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9 };
my_memcpy(arr + 3, arr, 16);
return 0;
}
正常情况下结果应该是 {1,2,3,1,2,3,4,8,9} 对吧
然而:
![](https://img-blog.csdnimg.cn/img_convert/442f3a17867099a7f9c1cc70347d91c4.png)
事实上结果是 {1,2,3,1,2,3,1,8,9}
这是为什么呢?
别急别急,让我们来画图分析一下
![](https://img-blog.csdnimg.cn/img_convert/00a0fd02eca2575f7bf287c8f1ee1e24.png)
当arr[3]要复制给arr[6]的时候,arr[3]已经从原来的4变成了1,所以就会出现上述情况
而memmove就完美解决了这种问题
那它是如何解决的呢?:
dest的地址小于src的地址时我们就采用从前向后拷贝,当我们dest的地址大于src的地址我们采用的从后向前拷贝
如此一来,覆盖的问题就得到了解决
然而
会出现问题的只是my_memcpy,库函数中的memcpy则不会出现这种问题,但是如此一来memmove的存在就好像没有意义,但是这事绝对不可能的,设计开发者不会闲着没事设计两个一模一样的函数,memmove的存在一定有它存在的价值,
因此我们猜想:memcpy的出现比memmove早,因为出现了一些问题于是又设计出了memmove,而为了完善memcpy,于是对其进行了修改,上述问题就不会发生了。
Memmove
memmove的功能
memmove () 函数将 n 个字节从内存区域 src 复制到内存区域 dest, 但是相比于 memcpy 函数不同的是,他的内存区域可能会重叠:复制的过程就好比是将 src 中的字节首先被复制到一个不重叠的临时数组中 src 或 dest 中,然后将字节从临时数组复制到 dest
库函数中的memmove
![](https://img-blog.csdnimg.cn/img_convert/dda11894c05ace78e57d86cf29cccd3d.png)
函数与memcpy一样包含三个参数(复制对象,复制内容,复制长度),返回值也是无符号指针
cplusplus中的解释:
![](https://img-blog.csdnimg.cn/img_convert/59ab7f047dd4a78116954119aa595641.png)
memmove的具体运用
就用<过渡>中提到的情况为例
#include<string.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9 };
memmove(arr + 3, arr, 16);
return 0;
}
![](https://img-blog.csdnimg.cn/img_convert/6d25e9296c4060067c8c32c9d2b68261.png)
结果确实是 {1,2,3,1,2,3,4,8,9}
注意
同上,目的地dest[]必须足够大,否则越界
memmove的模拟实现
void* my_memmove(void* dest, const void* src, size_t count)
{
assert(dest && src);//断言,确保指针有效
char* ret = (char*)dest;//定义返回值
if (dest < src)//dest的地址小于src的地址时我们就采用从前向后拷贝
{
while (count--)
{
*(char*)dest = *(char*)src;
src = (char*)src + 1;
dest = (char*)dest + 1;
}
}
else//dest的地址大于src的地址时我们就采用从后向前拷贝
{
while (count--)
{
*((char*)dest+count) = *((char*)src+count);
}
}
return ret;
}
总结:
从库函数strcpy到memcpy再到memmove的过程,我们可以看出,事物的发展是不断完善的,没有一个事物生来就是完美的,学习就是让不完美的我们不断完善的一个过程,也许我们永远也都达不到完美,但是只要活在当下,每天进步一点,就足够了,所以,诸君尽管大胆的向前走,明天会更好