C语言函数_string.h 之 内存copy函数memmove
2015-05-01 22:26
701 查看
上一篇文章用C语言实现了最简单(逐字节复制)的内存复制函数memcpy(),但memcpy()函数有一个问题是:不能保证src和dest所指内存区域不会出现重叠的情况,没有重叠区域,只能靠程序员来保证。所谓的内存重叠,就是复制的目的地址的首地址出现在源地址范围内,如下图:上面的表示没有内存重叠,下面的则表示有内存重叠
【图片来自网上】
程序例子,比如:
那么&p[3]串的值就会变成"abcabijk",为什么红色部分会是abcab呢,因为p[3]-p[5]的值已经变成了“abc”,因为内存重叠,所以memcpy()函数就会得到错误的结果,虽然程序运行时并不会崩溃。事实上,无论是VS还是GCC,它们的库函数memcpy都已经处理了内存重叠的问题,在GCC编译器下,上面代码得出的结果是"abcdeijk"。
在这篇文章中介绍另一个内存复制函数memmove(),它将会处理内存重叠的问题。
函数原型:void *memmove(void *dest, const void *src, size_t count)
函数功能:由src所指的内存区域复制count个字节到dest所指的内存区域
memmove()处理内存重叠的方法:如果出现内存重叠,则从待复制数据片段的末端开始复制;否则像memcpy()一样操作。
代码:
<= src || (char*)dest >= ((char*)src + count),由此看来上面对“内存重叠”的理解有bug,因为当dest == src的时候dest也算是在src的范围内,但这是没关系的,复制的结果就是把同一个地址上的值置为相同的一个值。当上表达式成立的时候,执行相当于memcpy()函数的功能。如果存在内存重叠,则从需要复制的内存末端开始复制,如下图中下面部分,内存重叠部分为3-4两个字节部分,其实count是5,那么从末端开始时,src就应该从src+count-1=4的位置开始,dst就应该从dst+count-1=7的位置开始。所以:4->7,
3->6, 2->5, 1->4, 0->3(a->b表示把a内存的值赋给b内存地址),明显地看到,4->7, 3->6已经把src中3,4的值存放在dst上了,当把地址0,1的内容赋给地址3,4的时候不会对dst上6,7的内容造成影响,虽然原来的数据不再是原来的数据(dest+size>=src),但拷贝的数据是原来的数据,所以得到的结果是正确的。
与memcpy()的区别
1、函数memcpy() 从src 指向的区域向dest指向的区域复制count个字符,如果两内存有重叠,则该函数的行为是未定义的;而memmove(),如果两内存存在重叠时,赋值仍正确进行。
2、memcpy函数假设要复制的内存区域不存在重叠,如果你能确保你进行复制操作的的内存区域没有任何重叠,可以直接用memcpy;如果你不能保证是否有重叠,为了确保复制的正确性,你必须用memmove。
【图片来自网上】
程序例子,比如:
char p[20] = "abcdefghijk"; memcpy(&p[3], p, 5); printf(“%s", &p[3]);
那么&p[3]串的值就会变成"abcabijk",为什么红色部分会是abcab呢,因为p[3]-p[5]的值已经变成了“abc”,因为内存重叠,所以memcpy()函数就会得到错误的结果,虽然程序运行时并不会崩溃。事实上,无论是VS还是GCC,它们的库函数memcpy都已经处理了内存重叠的问题,在GCC编译器下,上面代码得出的结果是"abcdeijk"。
在这篇文章中介绍另一个内存复制函数memmove(),它将会处理内存重叠的问题。
函数原型:void *memmove(void *dest, const void *src, size_t count)
函数功能:由src所指的内存区域复制count个字节到dest所指的内存区域
memmove()处理内存重叠的方法:如果出现内存重叠,则从待复制数据片段的末端开始复制;否则像memcpy()一样操作。
代码:
void * memmove( void *dest, const void *src, size_t count) { assert((NULL != dest) && (NULL != source)); void *ret = dest; if( dest <= src || (char*)dest >= ((char*)src + count)) { while( count --) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } } else { dest = (char*)dest + count - 1; src = (char*)src + count - 1; while( count --) { *(char*)dest = *(char*)src; dest = (char*)dest - 1; src = (char*)src - 1; } } return ret; }程序先判断目的地址和源地址是否有重叠,判断表达式为:dest
<= src || (char*)dest >= ((char*)src + count),由此看来上面对“内存重叠”的理解有bug,因为当dest == src的时候dest也算是在src的范围内,但这是没关系的,复制的结果就是把同一个地址上的值置为相同的一个值。当上表达式成立的时候,执行相当于memcpy()函数的功能。如果存在内存重叠,则从需要复制的内存末端开始复制,如下图中下面部分,内存重叠部分为3-4两个字节部分,其实count是5,那么从末端开始时,src就应该从src+count-1=4的位置开始,dst就应该从dst+count-1=7的位置开始。所以:4->7,
3->6, 2->5, 1->4, 0->3(a->b表示把a内存的值赋给b内存地址),明显地看到,4->7, 3->6已经把src中3,4的值存放在dst上了,当把地址0,1的内容赋给地址3,4的时候不会对dst上6,7的内容造成影响,虽然原来的数据不再是原来的数据(dest+size>=src),但拷贝的数据是原来的数据,所以得到的结果是正确的。
与memcpy()的区别
1、函数memcpy() 从src 指向的区域向dest指向的区域复制count个字符,如果两内存有重叠,则该函数的行为是未定义的;而memmove(),如果两内存存在重叠时,赋值仍正确进行。
2、memcpy函数假设要复制的内存区域不存在重叠,如果你能确保你进行复制操作的的内存区域没有任何重叠,可以直接用memcpy;如果你不能保证是否有重叠,为了确保复制的正确性,你必须用memmove。
相关文章推荐
- C语言函数_string.h 之 内存search函数memchr
- C语言函数string.h 之 内存copy函数memcpy
- C语言函数_string.h 之 内存comparison函数memcmp
- C语言函数_string.h 之 内存填充函数memset
- Jackson 触发的String.intern() bug, 导致内存持续增加,JVM-Java内存泄漏
- Java之内存分析和String对象
- JVM深入浅出(三) -- String.intern的内存分配和工作机制
- 标准C++类std::string的内存共享和Copy-On-Write(写时拷贝)
- 使用String.intern减少内存使用
- stringstream clear() 清除内存的误区
- 字符串string和内存流MemoryStream及比特数组byte[]互转
- Java中String类型笔试题目,对象声明与创建及在内存中的存贮
- java内存分配和String类型的深度解析
- string的内存分配引发的思考
- String.intern()内存分析
- String intern()的作用及String内存机制
- 关于String内存分配的深入探讨 (转)
- 【转】警惕多线程环境string、vector、protobuf等自增长数据结构的隐性内存泄露
- 字符串string、内存流Memory、Stream 流 及比特数组byte[] 互转方法汇总
- java内存分配和String类型的深度解析