淺析memmove函數之內存重疊

  話說上一節說到memcpy函數實現沒有考慮內存重疊問題,難道C庫函數故意留一個bug嗎?當然不是了,memmove函數就彌補了這個不足哦,是不是以前一直覺得這兩個函數沒啥區別呢,實際上區別大了去了,下面詳細道來。
  老話常談,C語言中操作內存的最小單元當屬字節,假如100個字節的內存需要搬運,恰巧的是目的內存的100字節中,包含了源地址中的50個,假如依舊按照memcpy方式來的話,保證那50個字節重疊部分不是你想要的,不信就弄一個100字節的數組試一試嘍。

1. 內存重疊原理分析

  這裏借用一個圖來用一下,圖上有該作者的鏈接,有興趣者可以訪問哦,替他打一波廣告。
這裏寫圖片描述
  圖中展示了兩種內存重疊的情況,乍一看還挺複雜,如何快速識破他的真面目呢?其實,搞清楚兩點,一切反動派都是紙老虎,哈哈

  1. 內存是否重疊 做減法
      內存地址間距(src-dst)能否裝下count字節,裝不下的話,src拷貝後肯定會佔用dst的部分內存啦,這裏只需做減法,比大小,不看正負
  2. 拷貝過程 看方向
      搬磚工從src位置一塊磚一塊磚的搬到dst位置,從src到dst這個方向與內存增長方向一致,那完了,搬着石頭砸自己的腳,肯定破壞了自己的內容,腳疼啊。若二者方向相反,src已經搬完了,騰空了內存,正好供dst用啊,不重疊嘍,感覺說的好囉嗦,有助理解吧,對應圖例,圖一重疊但不影響搬運內容,圖二重疊但影響內容,我們memmove函數致力於解決這個問題。

2. memmove函數原型

先看函數原型吧,好眼熟,跟memcpy一摸一樣呀,難道眼花了,沒錯就是一樣。

void* my_memmove(void* dst, const void* src, unsigned int size)

  面對上圖中內存重疊的情況,我們需要對這種情況進行判斷,然後針對拷貝方向與內存增長方向一致的情況做出討論和處理,怎麼處理呢?再想想我們的兩條原則,第一條原則內存的相對位置我們可改變不了,也就是間距,這是固定的。好吧那就只有改變第二條了,第二條什麼來着,方向,方向,方向,重要的說三遍。
  好吧,方向怎麼改呢?內存方向?那改屁啊,內存自誕生起就定好的,那就只能改他了,哈哈哈,拷貝方向,長話短說,我先拷貝src的最高字節到dst的最高字節,依次拷次高字節,就是反着拷,來看具體實現:

void *memmove(void *dest,void *src,unsigned int size)
{
        char *d=(char *)dest; //牛鬼蛇神都得轉存成字節,字節是最基本拷貝單元呢
        char *s=(char*)src;    //源地址和目的地址都得轉,別忘了
        if(d==NULL||s==NULL||size<0) return NULL; //給我空地址,我可不要,扔回去
        if(s<d&&s+size>d){      //兩個條件,方向和間距嘍
            s=s+size-1;             //這個是重疊第二種情況,需要調整拷貝方向
            d=d+size-1;             //把指針指向需要拷貝的最末字節(以前是最開始字節)
            while(size--) *d--=*s--; //搬一個減一個地址,直到完成所有拷貝
        }else{
            while(size--) *d++=*s++;  //這是第一種情況,及不重疊的情況哦
        }
        return dest;
}

哈哈,一氣呵成,細細思考這些細節,還是耐人尋味的,這就是C語言的魅力吧,換個思路,每個細胞裏面都包含了母體的全部信息,一葉知秋,希望能夠通過這些細緻的思考,迅速提示對C語言的深入認識嘍

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章