C++中的內存重疊問題


  • 內存重疊,直到做到一個筆試題才知道了什么是內存重疊。先上題目吧,是一個淘寶的筆試題,當時有點懵,不知道這個名詞是啥子意思。
  • 題目:補充下面函數代碼:
    如果兩段內存重疊,用memcpy函數可能會導致行為未定義。 而memmove函數能夠避免這種問題,下面是一種實現方式,請補充代碼。
    • #include <iostream>
      using namespace std;
      void* memmove(void* str1,const void* str2,size_t n)
      {
          char* pStr1= (char*) str1;
          const char* pStr2=(const char*)str2;
          if  ( ) {
              for(size_t i=0;i!=n;++i){
                  *(pStr1++)=*(pStr2++);
              }
          }
          else{
              pStr1+=n-1;
              pStr2+=n-1;
              for(size_t i=0;i!=n;++i){
                  *(pStr1--)=*(pStr2--);
              }
          }
          return ( );
      }
      
      在上面的兩個括號中插入對應的內容
      
      答案:pstr1<pstr2    str1


      在這里我理解的內存重疊大致上應該是在strcpy以及memcpy等內存拷貝函數出現的問題。strcpy函數內存重疊可能會使程序崩潰,這里我先講一個比較簡單的例子來看一下。

    • #include <string.h>
      #include <stdlib.h>
      #include <stdio.h>
      int main(){
              char *p = NULL;
              p = (char *)malloc(10);
              memcpy(p,"1234679",strlen("1246789"));
              printf("before p = %s/n", p);
              strcpy(p+1,p);//這重疊了
              printf("after p = %s/n", p);
              free(p);
      }

       上面的這個例子中發生了內存重疊問題,這就導致了拷貝發生了異常,不會拷貝一樣的數據。我跑這個代碼的時候居然沒有報錯,程序也沒炸,我也就有點好奇了。按理來memcpy,strcpy這兩個函數沒有對內存重疊進行處理。使用這兩個函數的時候只有程序員自己保證源地址與目標地址內存不重疊。所以當會發生內存重疊的時候最好使用memmov函數進行內存拷貝。

      自己查了一些資料,原來memcpy有一個長度參數,只拷貝cnt個字節就結束了,所以會得到正確的結果,但是strcpy函數知道拷貝到\0這個標志符才會結束,所以就會導致程序崩潰了。

  •   memcpy和memmov函數原型和區別
    •   1.memmove

      函數原型:void *memmove(void *dest, const void *source, size_t count)

      返回值說明:返回指向dest的void *指針

      參數說明:dest,source分別為目標串和源串的首地址。count為要移動的字符的個數

      函數說明:memmove用於從source拷貝count個字符到dest,如果目標區域和源區域有重疊的話,memmove能夠保證源串在被覆蓋之前將重疊區域的字節拷貝到目標區域中。

    •   2.memcpy

      函數原型:void *memcpy(void *dest, const void *source, size_t count);

      返回值說明:返回指向dest的void *指針

      函數說明:memcpy功能和memmove相同,但是memcpy中dest和source中的區域不能重疊,否則會出現未知結果。


  •   實現一個memmov函數
    •   
      #include <iostream>
      #include <string.h>
      using namespace std;
      
      void *memmove(void * dst, const void *src, size_t count){
          //特殊情況錯誤處理
          if (src == NULL || dst == NULL)
              return NULL;
          unsigned char *pdst = (unsigned char *)dst;
          const unsigned char *psrc = (const unsigned char *)src;
      
          //判斷內存是否重疊
          bool flag1 = (pdst >= psrc && pdst < psrc + count);
          bool flag2 = (psrc >= pdst && psrc < pdst + count);
          //上面兩個標志其中有一個成立,保證內存並不是重疊的,與拷貝的長度有關
      
          if (flag1 || flag2){//內存重疊
              //倒序拷貝
              while (count){
                  *(pdst + count - 1) = *(psrc + count - 1);
                  count--;
              }
          }
          else{//不重疊
              while (count--){
                  *pdst = *psrc;
                  pdst++;
                  psrc++;
              }
          }
          return dst;
      }
      
      int main(){
          //內存重疊的情況
          char str[] = "hello world";
          memmove(str + 3, str, 8);
          cout<<"memmove result is :"<<str<<endl;
      
          //內存不重疊
          char str2[] = "hello world";
          char str3[] = "you are ";
          memmove(str2, str3, 8);
          cout<<"memmove result is :"<<str2<<endl;
      }

       

      實現思路就是要去判斷是否內存重疊,重疊的話就倒序的拷貝,非重疊的話就從前往后拷貝就行了。這個例子也很好的解釋了前面的那個題目的情況。其實前面的那個情況還可以用一個圖片來解釋。


    • 這張圖片就是這兩種情況的實現。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM