对内存重叠的深入认识
內(nèi)存重疊:拷貝的目的地址在源地址范圍內(nèi)。所謂內(nèi)存重疊就是拷貝的目的地址和源地址有重疊。
在函數(shù)strcpy和函數(shù)memcpy都沒有對內(nèi)存重疊做處理的,使用這兩個(gè)函數(shù)的時(shí)候只有程序員自己保證源地址和目標(biāo)地址不重疊,或者使用memmove函數(shù)進(jìn)行內(nèi)存拷貝。
memmove函數(shù)對內(nèi)存重疊做了處理。
現(xiàn)在來看函數(shù)strcpy
原型:extern char *strcpy(char *dest,char *source);
功能:把source所指由NULL結(jié)束的字符串復(fù)制到dest所指的數(shù)組中。
說明:source和dest所指內(nèi)存區(qū)域不可以重疊且dest必須有足夠的空間來容納source的字符串。
返回指向dest的指針。
重疊從兩方面考慮:
(1).dest數(shù)據(jù)覆蓋了source; 如:dest(8byte) 地址:1000
source(8byte) 地址:1002
(2).dest所指的區(qū)域本來就是source的一部分; 如:dest(8byte) 地址:1000
source(8byte) 地址:0998
例如:針對第一種交叉情況情況,dst<src且dst+count>src,memcpy和memmove的結(jié)果是一樣的。請看下面的例子講解:
string s = "hello world";
memmove(&s[0],&s[5],10);
舉個(gè)內(nèi)存重疊環(huán)境的例子:
int main()
{char *p = NULL;
p=(char*)malloc(100);
memcpy(p,"123456789",strlen("123456789")); //會等到錯(cuò)誤的結(jié)果,有一個(gè)長度參數(shù),只能拷貝cnt個(gè)
//字節(jié)就結(jié)束了
printf("before p =%s\n",p);
strcpy(p+1,p); //注意:這里重疊了,而strcpy是根據(jù)判斷原串中的'\0'
printf("after p =%s\n",p);
free(p);
}
1.下面來看strcpy()原型寫法: 字符串拷貝.?
char *strcpy(char *strDest, const char *strSrc)
{
assert((strDest!=NULL) && (strSrc !=NULL));
char *address = strDest;?
while( (*strDest++ = * strSrc++)·1 != '/0')?
NULL ;?
return address ;?
}
2.下面來看下memcpy函數(shù)的原型寫法:內(nèi)存拷貝
void *memcpy(void *dest, const void *source, size_t count)
{
assert((NULL != dest) && (NULL != source));
char *tmp_dest = (char *)dest;
char *tmp_source = (char *)source;
while(count --)//不對是否存在重疊區(qū)域進(jìn)行判斷
*tmp_dest ++ = *tmp_source ++;
return dest;
}
3.下面來看下memmove函數(shù)的原型寫法:
void *memmove(void *dest, const void *source, size_t count)
{
assert((NULL != dest) && (NULL != source));
char *tmp_source, *tmp_dest;
tmp_source = (char *)source;
tmp_dest = (char *)dest;
if((dest + count<source) || (source + count) <dest))
{// 如果沒有重疊區(qū)域
while(count--)
*tmp_dest++ = *tmp_source++;
}
else
{ //如果有重疊(反向拷貝)
tmp_source += count - 1;
tmp_dest += count - 1;
while(count--)
*--tmp_dest = *--tmp;
}
return dest;
}
深入分析:
void *memcpy(void *dst, const void *src, size_t count):
void *memmove(void *dst, const void *src, size_t count);
先看一個(gè)測試:
#include <string.h>
#include <stdio.h>
int main()
{ int a[10];
for(int i=0; i < 10; i++)
a[i] = i;
memcpy (&a[4],a,sizeof(int)*6); //結(jié)果為:1 2 3 0 1 2 3 0 1
//memcpy(&a[4], a, sizeof(int)*6); //結(jié)果為:1 2 3 0 1 2 3 0 1(vc下和下面一個(gè)相同)
//MemMove(&a[4],a,sizeof(int)*6); //結(jié)果為:1 2 3 0 1 2 3 4 5
//memmove(&a[4],a,sizeof(int)*6); //結(jié)果為:1 2 3 0 1 2 3 4 5
//MemMove(a,&a[4],sizeof(int)*6); //結(jié)果為:5 6 7 8 9 6 7 8 9
//memmove(a, &a[4], sizeof(int)*6);//結(jié)果為:5 6 7 8 9 6 7 8 9
//memcpy(a, &a[4], sizeof(int)*6); //結(jié)果為:5 6 7 8 9 6 7 8 9
//MemCopy(a,&a[4],sizeof(int)*6); //結(jié)果為:5 6 7 8 9 6 7 8 9
for(i = 0; i < 10; i++)
printf("%d ",a[i]);
printf("/n");
return 0;
}
它們都是從src所指向的內(nèi)存中復(fù)制count個(gè)字節(jié)到dst所指內(nèi)存中,并返回dst的值。當(dāng)源內(nèi)存區(qū)域和目標(biāo)內(nèi)存區(qū)域無交叉時(shí),兩者的結(jié)果都是一樣的。但有交叉時(shí)不一樣。源內(nèi)存和目標(biāo)內(nèi)存交叉的情況有以下兩種:(左邊為低地址)
即:dst<=src 且 dst+count>src
?針對第一種交叉情況情況,dst<=src且dst+count>src,memcpy和memmove的結(jié)果是一樣的。請看下面的例子講解:
int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
memcpy(a, a+4, sizeof(int)*6);和
memmove(a, a+4, sizeof(int)*6);結(jié)果一樣,都是:4567896789
?針對第二種情況,src<dst且src+count>dst,memcpy和memmove的結(jié)果是不一樣的。請看下面的例子:
int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
memcpy(a+4, a, sizeof(int)*6) 結(jié)果按照分析應(yīng)該是:0123012301
但是在vs2005上運(yùn)行卻是:0123012345(有知道的,請告訴我原因)
memmove(a+4, a, sizeof(int)*6) 結(jié)果是:0123012345
總結(jié):
1. 當(dāng) src 和 dest 所指內(nèi)存區(qū)有重疊時(shí),memmove 相對 memcpy 能提供保證:保證能將 src 所指內(nèi)存區(qū)的前 n 個(gè)字節(jié)正確的拷貝到 dest 所指內(nèi)存中;
2. 當(dāng) src 地址比 dest 地址低時(shí),兩者結(jié)果一樣。換句話說,memmove 與 memcpy 的區(qū)別僅僅體現(xiàn)在 dest 的頭部和 src 的尾部有重疊的情況下;
綜上所述在進(jìn)行內(nèi)存重疊的考慮時(shí),strcpy,memcpy都要做一個(gè)內(nèi)存重疊的判斷:
對于memcpy需要加上一個(gè)斷言:Assert(dst<=src || src+count<dst);
source和dest所指內(nèi)存區(qū)域不可以重疊且dest必須有足夠的空間來容納source的字符串。
返回指向dest的指針。
對于strcpy需要加上一個(gè)斷言:
int count = strlen(src) + 1;//src length
Assert (dest<src || dest>(src+count))
考慮了內(nèi)存重疊的內(nèi)存拷貝函數(shù) memcpy?,相當(dāng)于memmove
考慮內(nèi)存重疊的字符串拷貝函數(shù)strcpy
char * strcpy(char *dest, const char *src)
{
??? char *d = dest; //backup input
??? char *s = src;
??? int count = 0;
??? assert(dest); //非空指針檢查
??? assert(src);
??? if(src == dest)
???????? return src;
??? count = strlen(src) + 1;//src length
??? if(count<=1)
???????? return 0; //empty src
??? if(dest<src || dest>(src+count))
??? {
???????? while(count--)
???????????? *d++ = *s++;
??? }
??? else //dest 位于src+count中間,
??? {
???????? d = dest+count;
???????? s = src+count;
???????? while(count--)
???????????? *d-- = *s--; //倒過來拷貝
??? }
總結(jié)
以上是生活随笔為你收集整理的对内存重叠的深入认识的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Oracle view 小结片段
- 下一篇: Visual Studio 2012资源