四个拷贝函数的分析与实现

概述:

1:strncpy和strncpy主要是用于字符串的拷贝。

2:而memcpy()和memmove()则适用于所有的数据类型。

3: memcpy()和memmove()这两者的区别在于内存重叠的处理。

4:本文给出的代码都是基于函数的功能所写的代码,不一定就是官方的实现代码。但是实现的功能是一样的。

展开:

/*************************************************/

1.strcpy()函数

/*************************************************/

1 原型

char *strcpy(char* dest, const char *src);

2 定义
把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间

3 拷贝终止条件

当src字符结束时,把src末尾的’\0’也拷贝过来,复制结束。

4 代码实现
自己取函数名为mystrcpy();

char * mystrcpy(char *dst,const char* src)
//源字符串不能改变,所以const修饰
{
    assert(dst!=NULL&&src!=NULL);//断言方式确保指针不为空
    char* p=dst;
    while((*p++=*src++)!='\0');//逐个复制 直到*src='\0',注意src末尾的'\0'也要拷贝过来
    return dst;//返回的是目标字符串的首地址dst,在这里不能返回指针p
}

/*************************************************/

2.strncpy()函数

/*************************************************/

1 原型

strncpy()的原型是

char* strncpy(char*dest, char*src, size_t n);

其中:n为指定的拷贝的字节长度。

2 定义

:(c/c++)复制字符串src中的内容(字符,数字、汉字….)到字符串dest中,复制多少由size_tn的值决定。如果src的前n个字符不含NULL字符,则结果不会以NULL字符结束。
如果n<src的长度,只是将src的前n个字符复制到dest的前n个字符,不自动添加’\0’,也就是结果dest不包括’\0’,需要再手动添加一个’\0’。如果src的长度小于n个字节,则以NULL填充dest直到复制完n个字节。src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符长度+’\0’。

3 拷贝终止条件

3-1:n<=strlen(src)+1,复制n个字符,不自动加'\0'
3-2: n>strlen(src)+1,复制完src之后,因为复制次数还没有达到n,所以后面的每一次都是往dst补零,直到复制次数到达n.

代码实现

char* mystrncpy(char* dst, const char*src ,unsigned int n)
{
    assert(dst!=NULL&&src!=NULL);//断言
    char *pdst=dst;
    while(n&&(*pdst++=*src++)!='\0'){n--;}//注意此处n--不要放在while()的比较表达式里面。
    if(n)
    {
        while(n--)
        {
            *pdst++='\0';
        }
    }
    return dst;
}

/*************************************************/

3.memcpy()函数

/*************************************************/

1原型:

memcpy()的原型是

void *memcpy(void *dest, const void *src, size_t n);

其中:n为指定的拷贝的字节长度。

2定义

2-1 从源src所指的内存地址的起始位置开始拷贝n个字节到目标dest所指的内存地址的起始位置中.
2-2 注意:source和destin都不一定是数组,任意的可读写的空间均可

3拷贝终止条件:

复制次数达到n

4代码实现

void *mymemcpy(void *dst,const void *src,unsigned int num)//没有考虑内存重叠。
{
    assert(dst!=NULL&&src!=NULL);
    char*pdst=(char*)dst;
    const char *psrc=(const char*)src;
    while(num--)
    {
        *pdst++=*psrc++;
    }
    return dst; 
}

/******************************************/
思考
/**********************************************/
上面的memcpy()看似没有什么问题,但是当dst与src发生内存重叠时,就会出现问题了。
(这里需要说明的是,有些IDE像vs2010等就已经考虑了这种情况了,所以你所调用的memcpy就是我们下文要说的memmove.但是c标准没有规定一定要对memcpy进行内存重叠的考虑。所以你可能会发现有的编译环境下memcpy考虑了内存重叠,有些则没有考虑。这里我们说的就是没有考虑的情况。)
举个例子。

char str[]="abbbb";
memcpy(str+2,str,3);

我们本意是将str后面的三个连续的字符拷贝到str[2]往后的三个字节里面。就是说我们期待着的结果是:str[]=“ababb”;
但是当你执行之后,就会发现运行结果是str="ababa";原因很简单,内存发生了重叠了,原本的str[2]为’b’,在第一次拷贝时就被str[0]给替换成了’a’了,这就导致了最后一次拷贝时str[2]传递给str[4]的是’a’而不是‘b’。
什么时候会发生内存重叠呢?
满足 :
1:src<dst<=src+n会发生重叠,需要反序拷贝。
2:当src>dst或者dst<src+n则不会发生内存重叠,此时 正序拷贝/反序拷贝皆可。

所以可以进一步简化为

1:src>dst正序拷贝
2:src<dst反序拷贝
3:src=dst不拷贝

【上面提供了两种方式,本文我们简单点,将会采用第二种方式进行代码实现。】

下面就是memmove的真正介绍了。

/*************************************************/

4.memmove函数

/*************************************************/

1原型:

void *memmove( void* dest, const void* src, size_t n );

其中:n为指定的拷贝的字节长度。

2定义
memmove用于从src拷贝n个字节到dest,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中。但复制后src内容会被更改。但是当目标区域与源区域没有重叠则和memcpy函数功能相同。

3拷贝终止条件:

已经复制了n个字节

4代码实现

void* mymemmove(void *dst,const void *src,unsigned int n)
{
    assert(dst!=NULL&&src!=NULL);
    char* pdst=(char*)dst;
    const char* psrc=(const char*)src;
    if(pdst<psrc)//正序
    {
        while(n--)
        {
            *pdst++=*psrc++;
        }
    }
    else if(pdst>psrc)//反序
    {
        psrc=psrc+n-1;//边界要考虑好。
        pdst=pdst+n-1;
        while(n--)
        {
            *pdst--=*psrc--;
        }
    }
    return dst;

}

以上就是关于这几个函数的简单介绍,不对之处欢迎指正交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值