程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> strcpy,memcpy,memmove和內存重疊分析

strcpy,memcpy,memmove和內存重疊分析

編輯:C++入門知識

一:strcpy函數用法和實現:
[cpp]
  /* 
 
 GNU-C中的實現(節選): 
 
 */ 
 
 char* strcpy(char *d, const char *s)  
 
 {  
 
   char *r=d;  
 
   while((*d++=*s++));  
 
   return r;  
 
 }  

  /*

 GNU-C中的實現(節選):

 */

 char* strcpy(char *d, const char *s)

 {

   char *r=d;

   while((*d++=*s++));

   return r;

 }
有沒有發現這個實現並不是很好。。至少沒有判斷是不是為NULL。一個更好的版本:

[cpp]
char *strcpy(char *strDest, const char *strSrc) 

    assert((strDest!=NULL) && (strSrc !=NULL)); 
    char *address = strDest;  
    while( (*strDest++ = * strSrc++) != '\0')  
          
    return address ;  

 char *strcpy(char *strDest, const char *strSrc)
 {
  assert((strDest!=NULL) && (strSrc !=NULL));
  char *address = strDest;
  while( (*strDest++ = * strSrc++) != '\0')
   
  return address ;
 }
好好研究這段代碼其實寫的挺妙的。。(當然不是我寫的)。。

 

二:memcpy函數用法和實現:
從源src所指的內存地址的起始位置開始拷貝n個字節到目標dest所指的內存地址的起始位置中

微軟實現:

[cpp]
 void * __cdecl memcpy (void * dst,const void * src, size_t count )  
 {  
     void * ret = dst;  
#if defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC)           
     {          
         extern void RtlMoveMemory( void *, const void *, size_t count ); 
         RtlMoveMemory( dst, src, count ); 
     }  
#else  /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */           
     /*          * copy from lower addresses to higher addresses          */         
     while (count--) {  
         *(char *)dst = *(char *)src; 
         dst = (char *)dst + 1;  
         src = (char *)src + 1; 
     }  
#endif  /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */          
     return(ret); 
 }  

 void * __cdecl memcpy (void * dst,const void * src, size_t count )
 {
  void * ret = dst;
#if defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC)        
  {        
   extern void RtlMoveMemory( void *, const void *, size_t count );
   RtlMoveMemory( dst, src, count );
  }
#else  /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */        
  /*          * copy from lower addresses to higher addresses          */       
  while (count--) {
   *(char *)dst = *(char *)src;
   dst = (char *)dst + 1;
   src = (char *)src + 1;
  }
#endif  /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */       
  return(ret);
 }

寫的是不是也很好。。仿佛天衣無縫啊!。。但是你有沒有注意到一個問題:那就是內存重疊。。

什麼叫內存重疊呢?就是兩塊內存區域有一個共同的部分。。是不是很SB的解釋。。當存在這個問題的時候,那還和我們預期的那樣拷貝嗎?注意:理解了這個才是關鍵。。

例子:(引用網上一個例子)

[cpp]
int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 
 memcpy(a+4, a, sizeof(int)*6)  

int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
 memcpy(a+4, a, sizeof(int)*6) 如果照微軟實現的話,那麼輸出應該是0123012301(理解這個再忘下看吧。。)

而我們想要達到的目的是輸出:0123012345。。如果你實驗一下,確實也是輸出0123012345。。(對於這個問題,我也解釋不了)。為了避免這樣的情況發生,我們找到了一個更好的函數:memmove。。

 

三:memmove函數用法和實現
用法和memcpy函數一樣。。

實現:

[cpp]
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)) 
 {// 如果沒有重疊區域  
     while(count--) 
         *tmp_dest++ = *tmp_source++; 
 } 
 else 
 { //如果有重疊(反向拷貝)  
     tmp_source += count - 1; 
     tmp_dest += count - 1; 
     while(count--) 
         *--tmp_dest = *--tmp; 
 } 
 return dest; 

 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))
  {// 如果沒有重疊區域
   while(count--)
    *tmp_dest++ = *tmp_source++;
  }
  else
  { //如果有重疊(反向拷貝)
   tmp_source += count - 1;
   tmp_dest += count - 1;
   while(count--)
    *--tmp_dest = *--tmp;
  }
  return dest;
 }
一開始看到這段代碼的時候,覺得好神奇啊。後來發現,其實有個很大的漏洞(先不說那些小問題),那就是內存重疊有兩種情況。其實我覺得該作者也想到了,而且給出了解決辦法。,估計是粗心大意吧。
:src<dest&&src+count>dest;  dest<src&&dest+count>src。。但是網上那位作者沒有寫清楚,下面我就重寫一下上面的else部分:

[cpp]
else 
 {  
     //重疊1:(反向拷貝)  
     if(tmp_source<=tmp_dest&&tmp_source+count>=tmp_dest) 
     { 
         tmp_source += count - 1; 
         tmp_dest += count - 1; 
         while(count--) 
             *tmp_dest-- = *tmp_source--; 
     } 
      //重疊2:(正向拷貝)  
     else 
     { 
         while (count--) 
         { 
             *tmp_dest++=*tmp_source++; 
         } 
     } 
 } 

 else
  {
   //重疊1:(反向拷貝)
   if(tmp_source<=tmp_dest&&tmp_source+count>=tmp_dest)
   {
    tmp_source += count - 1;
    tmp_dest += count - 1;
    while(count--)
     *tmp_dest-- = *tmp_source--;
   }
    //重疊2:(正向拷貝)
   else
   {
    while (count--)
    {
     *tmp_dest++=*tmp_source++;
    }
   }
  }
但寫完之後發現傻逼了。。後面的那個else不可以和前面的那個if寫在一起麼?所以優化後的完整代碼:

[cpp]
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(tmp_source<=tmp_dest&&tmp_source+count>=tmp_dest) 
 { 
     tmp_source += count - 1; 
     tmp_dest += count - 1; 
     while(count--) 
         *tmp_dest-- = *tmp_source--; 
 } 
 else 
 { 
     while (count--) 
     { 
         *tmp_dest++=*tmp_source++; 
     } 
 } 
 
 return tmp_dest; 

 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(tmp_source<=tmp_dest&&tmp_source+count>=tmp_dest)
  {
   tmp_source += count - 1;
   tmp_dest += count - 1;
   while(count--)
    *tmp_dest-- = *tmp_source--;
  }
  else
  {
   while (count--)
   {
    *tmp_dest++=*tmp_source++;
   }
  }
 
  return tmp_dest;
 }
 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved