memcpy和memmove()都是C語言中的庫函數,在頭文件string.h中,作用是拷貝一定長度的內存的內容,原型分別如下:
void *memcpy(void *dst, const void *src, size_t count);
void *memmove(void *dst, const void *src, size_t count);
他們的作用是一樣的,唯一的區別是,當內存發生局部重疊的時候,memmove保證拷貝的結果是正確的,memcpy不保證拷貝的結果的正確。
第一種情況下,拷貝重疊的區域不會出現問題,內容均可以正確的被拷貝。
第二種情況下,問題出現在右邊的兩個字節,這兩個字節的原來的內容首先就被覆蓋了,而且沒有保存。所以接下來拷貝的時候,拷貝的是已經被覆蓋的內容,顯然這是有問題的。
實際上,memcpy只是memmove的一個子集。
二者的c語言實現很簡單,有興趣的朋友可以去看看。在實際情況下,這兩個函數都是用匯編實現的。
memmove在copy兩個有重疊區域的內存時可以保證copy的正確,而memcopy就不行了,但memcopy比memmove的速度要快一些,如:
char s[] = "1234567890";
char* p1 = s;
char* p2 = s+2;
memcpy(p2, p1, 5)與memmove(p2, p1, 5)的結果就可能是不同的,memmove()可以將p1的頭5個字符"12345"正確拷貝至p2,而memcpy()的結果就不一定正確了
變態的命名
我們在寫程序時,一般講究見到變量的命名,就能讓別人基本知道該變量的含義。memcpy內存拷貝,沒有問題;memmove,內存移動?錯,如果這樣理解的話,那么這篇文章你就必須要好好看看了,memmove還是內存拷貝。那么既然memcpy和memmove二者都是內存拷貝,那二者究竟有什么區別呢?
先說memcpy
你有沒有好好的參加過一場C++筆試。讓你寫出memcpy的實現,這是多么常見的筆試題啊。現在,拿起你的演算紙和筆;是的,是筆和紙,不是讓你在你的IDE上寫。寫不出來?看下面吧:
void *memcpy(void *dest, const void *src, size_t count)
{
assert(dest != NULL || src != NULL);
char *tmp = (char *)dest;
char *p = (char *)src;
while (count--)
{
*tmp++ = *p++;
}
return dest;
}
memcpy的實現很簡單,一般在筆試時,出現寫源碼的題目,無非就是需要注意以下幾點:
1.確定函數原型;
2.判斷參數合法性;
3.邏輯實現(考慮各種情況,統稱邏輯實現);
4.錯誤處理。
當然了,我的這個沒有錯誤處理,也不需要錯誤處理。上面,我寫出了memcpy的實現源碼,實現原理如下圖所示:

這樣下去,上面的代碼會運行的很好,如果出現下面的情況呢?

i、n、k的內存和J、e、l的內存地址重合了,現在再使用上面的代碼進行copy時,會出現什么問題呢?你有沒有想過這個問題。如果沒有,那就現在想想,不急着閱讀下面的內容。
然后,我再留一個問題,上面的代碼中,為什么都需要將void *轉換成char *呢?比如:
char *tmp = (char *)dest;
可以留言回答哦。
再說memmove
memmove也是用來實現內存的直接拷貝的。說起這個命名,我個人覺的多少還是有點坑的。既然memmove也是用來內存數據移動的,那就先來看看memmove的實現源碼。
void *memmove(void *dest, const void *src, size_t count)
{
assert(dest != NULL || src != NULL)
if (dst < src)
{
char *p = (char *)dest;
char *q = (char *)src;
while (count--)
{
*p++ = *q++;
}
}
else
{
char *p = (char *)dest + count;
char *q = (char *)src + count;
while (count--)
{
*--p = *--q;
}
}
return dest;
}
從源碼看,memmove的確比memcpy復雜一些;再仔細一看,多了些什么?哦,多了一個else分支,而正是這個else分支,就處理了當src和dest的內存重合的問題。
memcpy和memmove的比較
從實現源碼中的確能看出一些貓膩,當出現了src和dest的內存有重合的時機時,memmove的處理規則是從后往前進行copy。當然了,重合的問題,需要考慮的以下兩種場合。

如圖所示,當出現(1)對應的情況時,就需要先從src的頭部開始復制;也就是memmove源碼中的if分支,這部分源碼和memcpy的實現是一致的;當出現(2)對應的情況時,就需要先從src的尾部開始復制,防止出現了覆蓋現象。這就是memmove比memcpy多的一個考慮點,所以說,在實際使用時,使用memmove是比memcpy更安全的。
