内存重叠之memcpy、memmove

本文详细解析了memcpy和memmove函数的功能与实现差异,重点阐述了在内存重叠情况下两者的处理方式,以及在Linux和Windows平台上的具体表现。

函数原型:

void *memcpy( void *dest, const void *src, size_t count );
void *memmove( void* dest, const void* src, size_t count );

1.memcpy和memmove相同点
都是用于从src拷贝count个字节到dest。

2.memcpy和memmove区别
如果目标区域和源区域有重叠的话:
memcpy不能够确保源串所在重叠区域在拷贝之前被覆盖。
memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,复制后src内容会被更改,当目标区域与源区域没有重叠则和memcpy函数功能相同。

但当源内存和目标内存存在重叠时,memcpy会出现错误,而memmove能正确地实施拷贝,但这也增加了一点点开销。

memmove的处理措施:
(1)当源内存的首地址等于目标内存的首地址时,不进行任何拷贝
(2)当源内存的首地址大于目标内存的首地址时,实行正向拷贝
(3)当源内存的首地址小于目标内存的首地址时,实行反向拷贝

3.Linux下的实现过程
linux下,两个函数都在头文件string.h中定义,函数原型为:

void * __cdecl memcpy ( void * dst,const void * src,size_t count);
void * __cdecl memmove ( void * dst,const void * src,size_t count);
 
//实现代码如下:
 
void * __cdecl memcpy ( void * dst,const void * src,size_t count){
         void * ret = dst;
 
         while (count--) {
        // 注意, memcpy函数没有处理dst和src区域是否重叠的问题
                   *(char *)dst = *(char *)src;
                   dst = (char *)dst + 1;
                   src = (char *)src + 1;
         }
 
         return(ret);
}
  
void * __cdecl memmove ( void * dst,const void * src,size_t count){
         void * ret = dst;
         if (dst <= src || (char *)dst >= ((char *)src + count)) {
                   // 若dst和src区域没有重叠,则从起始处开始逐一拷贝
                   while (count--){
                            *(char *)dst = *(char *)src;
                            dst = (char *)dst + 1;
                            src = (char *)src + 1;
                   }
         }
         else{
        // 若dst和src 区域交叉,则从尾部开始向起始位置拷贝,这样可以避免数据冲突
                   dst = (char *)dst + count - 1;
                   src = (char *)src + count - 1;
 
                   while (count--){
                            *(char *)dst = *(char *)src;
                            dst = (char *)dst - 1;
                            src = (char *)src - 1;
                   }
         }
 
         return(ret);
}

当src和dst区域没有重叠时,两个函数是完全一样的。否则,memcpy不能正常工作的,memmove是可以正常工作的。

4.Windows平台


#include <stdio.h>
#include <string.h>
 
/* VS2010, Windows XP, Debug模式下运行 */
int main(void)
{  
    void test_memfunc(void);
    test_memfunc();
 
    return 0;
}
 
void test_memfunc(void){
 
    char s1[] = "abcdefgefghijklmnopq";//首地址:0x0012ff48
    char s2[] = "123456789";//首地址:0x0012ff34
    char *c = NULL;
 
    int l = sizeof(s1);//数组s1长度为21
 
     /*
     内存重叠 : s2覆盖了原s1()的一部分空间。
     即: s2(0x0012ff34-0x0012ff49)   的内存地址范围和s1(0x0012ff48-0x0012ff5c)的内存地址范围发生重叠
     */
     c = memcpy(s2,s1,sizeof(s1));//改用memmove同样运行出错
 
     /*
     运行出错:变量s1损坏。
     Run-Time Check Failure #2 - Stack around the variable 's2' was corrupted.
     */
     printf("%s",s1);
}

windows平台下,当发生内存重叠的时候,都不能正常运行,运行栈被破坏,提示错误:Run-Time Check Failure #2 - Stack around the variable ‘s2’ was corrupted.

参考:

http://msdn.microsoft.com/en-us/magazine/cc301374.aspx

http://www.cnblogs.com/BeyondAnyTime/archive/2012/05/10/2495013.html

<think>好的,我现在需要回答用户关于memmovememcpy内存重叠情况下的行为差异的问题。首先,我需要回忆这两个函数的基本定义和用途。根据提供的引用资料,memcpy是直接将源内存的内容复制到目标内存,但当源和目标有重叠时,可能会出现问题,因为它不处理这种情况。而memmove则能正确处理内存重叠,确保复制的正确性。 接下来,我需要明确两者的区别。用户特别关注内存重叠时的行为,所以需要详细说明这种情况下两者的表现。根据引用[1]和[2],memcpy重叠时可能覆盖源数据,导致数据错误,而memmove会采用某种策略(比如从后向前复制)来避免这个问题。需要具体解释为什么memcpy会有问题,而memmove如何解决。 然后,我需要举例说明,这样用户更容易理解。比如,当源地址在目标地址之前,并且有部分重叠时,memcpy可能复制到已经被覆盖的数据,而memmove会先检查方向,选择正确的复制顺序。引用中提到的拷贝方向(从前向后或从后向前)是关键点,这里需要详细说明。 另外,需要注意函数原型和参数是否相同,根据引用[2]和[3],它们的原型是一样的,都是接受目标、源和字节数。但内部实现不同,这点要强调。 还要考虑性能方面,通常memcpy可能更快,因为它不需要处理重叠的情况,而memmove因为多了判断和处理,可能稍慢一些。不过用户的问题没有提到性能,但可能相关的后续问题会涉及,所以可以稍微提一下。 最后,根据用户的要求,生成相关问题。可能需要包括使用场景、如何选择、实现原理等。确保问题覆盖用户可能感兴趣的方面。 总结一下,回答的结构应该是:先总述两者的基本功能,再重点比较内存重叠时的差异,举例说明,最后提到性能差异和使用建议。同时,引用相关的引用资料来支持每个观点,比如引用[1]说明memcpy的问题,引用[2]和[3]说明memmove的处理方式。</think>### memmovememcpy内存重叠时的行为差异 **核心差异** $memcpy$和$memmove$均为C标准库中的内存复制函数,但**内存区域重叠时的处理方式不同**: 1. **$memcpy$**:直接按字节顺序复制(通常从前向后),**不检查内存重叠**。若源地址($src$)与目标地址($dest$)重叠,可能导致部分数据被覆盖前未正确复制[^1][^4]。 2. **$memmove$**:检测内存重叠情况,并选择**安全复制方向**(从前向后或从后向前),确保重叠区域的原始数据在覆盖前被正确复制[^2][^3]。 --- ### 具体行为分析 #### 1. 内存重叠场景示例 假设存在以下内存布局: ``` src: [A][B][C][D][E] dest: [C][D][E][F][G] ``` - **$memcpy$行为**:若从左向右逐字节复制,当复制到第3字节时,源区域的`C`已被修改为目标区域的`A`,导致后续数据错误。 - **$memmove$行为**:检测到$dest > src$且存在重叠,改为从右向左复制,先复制最右侧的`E`,最后复制`A`,保证原始数据完整性。 #### 2. 实现原理对比 $$ \text{memcpy伪代码:} \\ \begin{aligned} &\text{for } i = 0 \text{ to } count-1: \\ &\quad dest[i] = src[i] \end{aligned} $$ $$ \text{memmove伪代码:} \\ \begin{aligned} &\text{if } dest < src: \\ &\quad \text{正向复制(从左向右)} \\ &\text{else}: \\ &\quad \text{反向复制(从右向左)} \end{aligned} $$ --- ### 使用建议 - **优先用$memcpy$**:当明确无内存重叠时,性能更高(少一次条件判断)[^4]。 - **必须用$memmove$**:当内存区域可能重叠时(如数组元素位移),避免数据损坏[^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值