昨天嘗試手寫 memcpy 函數,測試通過後信心滿滿,但細究細節卻發現問題重重。查閱資料後才發現,自己寫的版本簡直「無法直視」。特此總結,以為教訓。
char *mymemcpy(char *dest, const char *src, int n) {
char *pdest = dest;
char *psrc = src;
for(; n > 0; n--) {
*pdest = *psrc;
pdest++; psrc++;
}
}
dest 指針,違背標準庫約定受「地址傳參可修改數據」影響,誤以為形參指針與實參指針等同。實際上:
destchar* 按字節拷貝void* 不能直接賦值或運算void *memcpy(void *dest, const void *src, size_t count) {
if (NULL == dest || NULL == src || count <= 0) return NULL;
while (count--) *dest++ = *src++;
return dest; // 錯誤:返回了遞增後的指針
}
編譯錯誤:error: invalid use of void expression —— void* 不能直接解引用賦值。
void *memcpy(void *dest, const void *src, size_t count) {
if (NULL == dest || NULL == src || count <= 0) return NULL;
while (count--) *(char *)dest++ = *(char *)src++;
return dest; // 仍錯誤:返回值應為原始 dest
}
void *memcpy(void *dest, const void *src, int count) {
void *ptr = dest; // 保存首地址用於返回
if (NULL == dest || NULL == src || count <= 0) return NULL;
while (count--) *(char *)dest++ = *(char *)src++;
return ptr; // 正確返回起始地址
}
關鍵修正點:
int count 而非 size_t:size_t 為無符號類型,count <= 0 僅判斷 ==0,傳入 -1 會變成巨大無符號數ptr = dest 確保返回正確起始地址void *memcpy(void *dst, const void *src, size_t num) {
int nchunks = num / sizeof(unsigned long); // 按 CPU 位寬拷貝
int slice = num % sizeof(unsigned long); // 剩餘按字節拷貝
unsigned long *s = (unsigned long *)src;
unsigned long *d = (unsigned long *)dst;
while (nchunks--) *d++ = *s++; // 大塊拷貝
while (slice--) *((char *)d++) = *((char *)s++); // 尾部字節拷貝
return dst;
}
潛在問題:若 dst 和 src 地址未對齊,效率反而下降。是否需先處理對齊?
內存重疊:目的地址落在源內存區間內,導致數據被覆蓋破壞。

源數據後部被新拷貝數據覆蓋,結果錯誤。

雖源數據被改變,但拷貝結果正確。
void *memcpy(void *dest, const void *src, size_t count) {
char *d, *s;
if (dest > src && dest < src + count) { // 重疊且 dest 在 src 後
d = (char *)(dest + count - 1);
s = (char *)(src + count - 1);
while (count--) *d-- = *s--; // 從高地址向低地址拷貝
} else {
d = dest; s = src;
while (count--) *d++ = *s++;
}
return dest;
}
memcpy 早期版本無重疊保護,為兼容性保留memmovememmove 名稱含「move」但實為拷貝,源數據不會消失char* 按字節操作