C語言-記憶體函式的實現(二)之memmove

god23bin發表於2021-04-27

C語言中的記憶體函式有如下這些

  • memcpy
  • memmove
  • memcmp
  • memset

下面看看memmove函式

memmove

為什麼會需要memmove函式?

int main() 
{
    int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
    int i = 0;
    // 想把12345 拷貝到 34567上去
    // 應該列印 1 2 1 2 3 4 5 8 9 10
    my_memcpy(arr + 2, arr, 20);

    for (i = 0; i < 10; i++) 
    {
    	printf("%d ", arr[i]);
    }
    // 但是輸出 1 2 1 2 1 2 1 8 9 10

    return 0;
}

上面會輸出 1 2 1 2 1 2 1 8 9 10,我們來看看為什麼會出現這樣的結果。

我這裡畫了張圖,方便理解。

出現1 2 1 2 1 1 2 8 9 10

因為拷貝的地方重疊了,使原來的資料(3 4 5)被覆蓋了,導致最後出來的結果不是我們想要的。

也就是說,如果拷貝的地方重疊了,那麼就會出現這種情況。

那麼如何解決呢?答案就是從後往前拷貝,指標從最後的地方開始操作。

還是上一張圖

從後往前拷貝解決1 2 1 2 1 1 2 8 9 10

這樣,就得出了我們想要的結果。

但是呢,也不能一概而論,就全部都是從後往前拷貝,還是得分情況的,具體就是看destinationsource的位置關係。

destination和source位置關係

回到最開始的問題,為什麼會需要memmove函式?,因為memmove這個函式可以處理這種重疊拷貝。

老規矩,我們還是看看文件是怎樣說的,如下

memmove文件

void * memmove ( void * destination, const void * source, size_t num );

Move block of memory

移動記憶體塊(移動記憶體資料)

Copies the values of num bytes from the location pointed by source to the memory block pointed by destination. Copying takes place as if an intermediate buffer were used, allowing the destination and source to overlap.

從source(源記憶體塊位置)直接指向的地方開始複製num個位元組的資料到destination指向的記憶體塊位置。然後複製就像使用了中間緩衝區一樣,允許destination和source重疊。

The underlying type of the objects pointed by both the source and destination pointers are irrelevant for this function; The result is a binary copy of the data.

這句話沒看懂,不影響我們學這個。

The function does not check for any terminating null character in source - it always copies exactly num bytes.

這個函式不會檢查'\0',不會遇到'\0'就停下來,它就只認識要複製的num個位元組資料。

To avoid overflows, the size of the arrays pointed by both the destination and source parameters, shall be at least num bytes.

為了避免溢位,這兩個陣列的大小至少為num個位元組。

可以看出,memmove和memcpy的唯一區別就是,memmove函式處理的源記憶體塊和目標記憶體塊是可以重疊的。
也就是說,如果源空間和目標空間出現重疊,就得使用memmove函式處理。

實現

斷言指標不為空是個好習慣~

void* my_memmove(void* dest, void* src, size_t num) 
{
    //dest落在了src的左邊,從前往後拷貝
    //dest落在了src的右邊,同時沒有超過那個重疊的邊界的時候,從後往前拷貝
    assert(dest != NULL);
    assert(src != NULL);
    void* rest = dest;
    // void* 不能直接解引用,那麼如何複製呢?
    // 給了num個位元組,也就是需要複製num個位元組
    // 那就轉換成char*,一個一個位元組的複製過去
    if (dest < src) 
    //if (dest < src || dest > (char*)src + num) 
    {
    	//dest落在了src的左邊,從前往後拷
    	while (num--)
    	{
    	    *(char*)dest = *(char*)src;
    	    //++(char*)dest;
    	    //++(char*)src;
    	    ((char*)dest)++;
    	    ((char*)src)++;
    	}
    }
    else 
    {
    	// 從後往前拷
    	// 找到最後一個位元組
    	while (num--) 
    	{
    	    *((char*)dest + num) = *((char*)src + num);
    	}

    }
    return rest;
}

最後,感謝螢幕前靚仔花寶貴的時間閱讀我這篇部落格~
本人水平有限,如有錯誤以及不足之處,歡迎靚仔指出。

相關文章