memcpy和memmove都是C語言的庫函數(shù),相比于strcpy和strncpy只能拷貝字符串?dāng)?shù)組,memcpy與memmove可以拷貝其它類型的數(shù)組,但是為什么要同時(shí)提供兩種方法呢?本文主要就是介紹這兩個(gè)函數(shù)的區(qū)別。
首先來看函數(shù)原型:
void *memcpy(void *restrict s1, const void *restrict s2, size_t n);
void *memmove(void *s1, const void *s2, size_t n);
這兩個(gè)函數(shù)都是將s2指向位置的n字節(jié)數(shù)據(jù)拷貝到s1指向的位置,區(qū)別就在于關(guān)鍵字restrict, memcpy假定兩塊內(nèi)存區(qū)域沒有數(shù)據(jù)重疊,而memmove沒有這個(gè)前提條件。如果復(fù)制的兩個(gè)區(qū)域存在重疊時(shí)使用memcpy,其結(jié)果是不可預(yù)知的,有可能成功也有可能失敗的,所以如果使用了memcpy,程序員自身必須確保兩塊內(nèi)存沒有重疊部分。
我們來看一組示例:
正常情況下,即使內(nèi)容有重疊,src的內(nèi)容也可以正確地被拷貝到了dest指向的空間。
這種情況下,src的地址小于dest的地址,拷貝前3個(gè)字節(jié)沒問題,但是拷貝第4,5個(gè)字節(jié)時(shí),原有的內(nèi)容已經(jīng)被src拷貝過來的字符覆蓋了,所以已經(jīng)丟失原來src的內(nèi)容,這很明顯就是問題所在。
memcpy的實(shí)現(xiàn)##
一般來說,memcpy的實(shí)現(xiàn)非常簡(jiǎn)單,只需要順序的循環(huán),把字節(jié)一個(gè)一個(gè)從src拷貝到dest就行:
#include <stddef.h> /* size_t */
void *memcpy(void *dest, const void *src, size_t n)
{
char *dp = dest;
const char *sp = src;
while (n--)
*dp++ = *sp++;
return dest;
}
memmove的實(shí)現(xiàn)##
memmove會(huì)對(duì)拷貝的數(shù)據(jù)作檢查,確保內(nèi)存沒有覆蓋,如果發(fā)現(xiàn)會(huì)覆蓋數(shù)據(jù),簡(jiǎn)單的實(shí)現(xiàn)是調(diào)轉(zhuǎn)開始拷貝的位置,從尾部開始拷貝:
#include <stddef.h> /* for size_t */
void *memmove(void *dest, const void *src, size_t n)
{
unsigned char *pd = dest;
const unsigned char *ps = src;
if (__np_anyptrlt(ps, pd))
for (pd += n, ps += n; n--;)
*--pd = *--ps;
else
while(n--)
*pd++ = *ps++;
return dest;
}
這里__np_anyptrlt
是一個(gè)簡(jiǎn)單的宏,用于結(jié)合拷貝的長(zhǎng)度檢測(cè)dest與src的位置,如果dest和src指向同樣的對(duì)象,且src比dest地址小,就需要從尾部開始拷貝。否則就和memcpy處理相同。
但是實(shí)際在C99實(shí)現(xiàn)中,是將內(nèi)容拷貝到臨時(shí)空間,再拷貝到目標(biāo)地址中:
#include <stddef.h> /* for size_t */
#include <stdlib.h> /* for memcpy */
void *memmove(void *dest, const void *src, size_t n)
{
unsigned char tmp[n];
memcpy(tmp,src,n);
memcpy(dest,tmp,n);
return dest;
}
由此可見memcpy的速度比memmove快一點(diǎn),如果使用者可以確定內(nèi)存不會(huì)重疊,則可以選用memcpy,否則memmove更安全一些。另外一個(gè)提示是第三個(gè)參數(shù)是拷貝的長(zhǎng)度,如果你是拷貝10個(gè)double類型的數(shù)值,要寫成sizeof(double)*10,而不僅僅是10。