详解库函数strcpy,memcpy与memmove以及函数的自我实现

作者:热爱编程的小y
专栏:C语言基础
座右铭:岁月漫长,然而值得等待

Strcpy

strcpy的功能

strcpy,即string copy(字符串复制)的缩写。
strcpy是一种C语言的 标准库函数,strcpy把从src地址开始且含有’\0’结束符的字符串复制到以dest开始的地址空间,返回值的类型为char*。

库函数中的strcpy

使用前需包含头文件<string.h>

是众多字符串函数中的一种

cplusplus中对strcpy的解释:

函数包含两个参数,即复制对象destination与复制内容source,返回值是一个char类型的指针

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;
}

此时dest内部前11项已经从全是"\0"变成了"hello world"

注意

目的地dest[]必须足够大即"[]"内的数字必须不小于src[]的元素个数,否则就会出现数组越界的情况

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

memcpy函数包含三个参数(复制对象,复制内容,复制的长度),返回值为无类型指针

cplusplus中对memcpy的解释:

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;
}

注意

同上,目的地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} 对吧

然而:

事实上结果是 {1,2,3,1,2,3,1,8,9}

这是为什么呢?

别急别急,让我们来画图分析一下

当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

函数与memcpy一样包含三个参数(复制对象,复制内容,复制长度),返回值也是无符号指针

cplusplus中的解释:

memmove的具体运用

就用<过渡>中提到的情况为例


#include<string.h>
int main()
{
    int arr[] = { 1,2,3,4,5,6,7,8,9 };
    memmove(arr + 3, arr, 16);
    return 0;
}

结果确实是 {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的过程,我们可以看出,事物的发展是不断完善的,没有一个事物生来就是完美的,学习就是让不完美的我们不断完善的一个过程,也许我们永远也都达不到完美,但是只要活在当下,每天进步一点,就足够了,所以,诸君尽管大胆的向前走,明天会更好

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谁在夜里看海.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值