天天看點

動手實作自己的memcpy和strcpy一、問題由來二、動手實作你的memcpy三、動手實作你的strcpy四、測試結果

目錄

一、問題由來

二、動手實作你的memcpy

1.單位元組拷貝

2.四位元組拷貝(提升效率)

三、動手實作你的strcpy

四、測試結果

一、問題由來

        C庫中已提供有原生的memcpy、memmove以及strcpy等函數,位于<string.h>頭檔案中。

void *memcpy(void *dst, const void *src, size_t n);
           

        上面是C庫中原生的memcpy聲明,該memcpy在dst與src兩者的記憶體位址不互相重疊時,可以得到正确的處理結果,而兩者的記憶體位址有重疊時,該接口并不保證處理結果的正确性。

        接着則是memmove在string.h中的聲明,memmove比memcpy更先進的原因則在于:它解決了記憶體重疊時的拷貝情形。但memmove在不同編譯器上的實作會有所不同,故memmove在記憶體不重疊時,不一定會比memcpy更快。

void *memmove(void *dst, const void *src, size_t count)
           

       再觀之strcpy的函數原型,發現其聲明與memcpy有幾分相似,從中也可以看出:memcpy和memmove比strcpy的适用範圍更大,strcpy隻是它們的特殊情形。

char *strcpy(char *dest, const char *src)
           

        說到此,不論是探索的需求,還是面試的需求,想必讀者已經迫不及待地想要手撕一遍memcpy和strcpy的實作了,here we go! (但先說好了,咱們隻是原理上的實作,而不是探索極緻的效率,真正的源碼情況會考慮到更多的情況。)

二、動手實作你的memcpy

1.單位元組拷貝

//-------------------------按單位元組拷貝(基礎實作)-------------------//
void* my_memcpy_byte(void* dest, const void* src, size_t n)
{
    if(dest == NULL || src == NULL) return NULL;
  
    char *pdst = (char*)dest;
    const char *psrc = (char*)src; 

    if( pdst > (psrc + n) || pdst < psrc) {  //不重疊 || 雖重疊但dst < src
        printf("no-overlap: \n");
        while(n--) *pdst++ = *psrc++;  //順序copy
    }
    else {  //重疊, 但src < dest
        printf("overlap: \n");
        pdst = dest + n - 1;  //定位到前n個位元組的末尾
        psrc = src + n - 1;

        while(n--) *pdst-- = *psrc--;  //逆序copy
    }

    return dest;
}
           

2.四位元組拷貝(提升效率)

//----------------------按4位元組拷貝(提升效率)-------------------//
void* my_memcpy(void* dest, const void* src, size_t n)
{
    if(dest == NULL || src == NULL) return NULL;

    int *pdst = (int*)dest;
    int *psrc = (int*)src;
    char *byte1, *byte2;
    int cnt1 = n / 4; //多少個4位元組
    int cnt2 = n % 4; //餘出多少個單位元組

    //pdst > psrc + n 這樣判斷會有問題,雖然按4位元組進行拷貝,但記憶體重疊情況還是按單個位元組來比較
    if( (char*)pdst > ((char*)psrc + n) || pdst < psrc) {  
        printf("no-overlap: \n");

        while(cnt1--) *pdst++ = *psrc++;  //4位元組拷貝
        
        byte1 = (char*)pdst;
        byte2 = (char*)psrc;
        while(cnt2--) *byte1++ = *byte2++;  //單位元組拷貝
    }
    else {
        printf("overlap: \n");

        byte1 = (char*)pdst + n - 1;
        byte2 = (char*)psrc + n - 1;
        while(cnt2--) *byte1-- = *byte2--;  //單位元組拷貝

        byte1++, byte2++;
        pdst = (int*)byte1 - 1;
        psrc = (int*)byte2 - 1;
        while(cnt1--) *pdst-- = *psrc--;   //4位元組拷貝
    }

    return dest;
}
           

三、動手實作你的strcpy

char* my_strcpy(char* dest, const char* src)
{
    if(dest == NULL || src == NULL) return NULL;

    char *psrc = (char*)src;
    int n = 0;
    while(*psrc) {  //得到長度
        n++;
        psrc++;
    }

    my_memcpy_byte(dest, src, n);

    return dest;
}
           

四、測試結果

附上整體代碼,以供讀者參考。本例完全脫離于<string.h>而實作,可放心食用。

#include<stdio.h>

//-------------------------按單位元組拷貝(基礎實作)-------------------//
void* my_memcpy_byte(void* dest, const void* src, size_t n)
{
    if(dest == NULL || src == NULL) return NULL;
  
    char *pdst = dest;
    const char *psrc = src; 

    if( pdst > (psrc + n) || pdst < psrc) {  //不重疊 || 雖重疊但dst < src
        printf("no-overlap: \n");
        while(n--) *pdst++ = *psrc++;  //順序copy
    }
    else {  //重疊且src < dest
        printf("overlap: \n");
        pdst = dest + n - 1;
        psrc = src + n - 1;

        while(n--) *pdst-- = *psrc--;  //逆序copy
    }

    return dest;
}

//----------------------按4位元組拷貝(提升效率)-------------------//
void* my_memcpy(void* dest, const void* src, size_t n)
{
    if(dest == NULL || src == NULL) return NULL;

    int *pdst = (int*)dest;
    int *psrc = (int*)src;
    char *byte1, *byte2;
    int cnt1 = n / 4; //多少個4位元組
    int cnt2 = n % 4; //餘出多少個單位元組

    //pdst > psrc + n 這樣判斷有問題,雖然按4位元組進行拷貝,但記憶體重疊情況還是按單個位元組來描述
    if( (char*)pdst > ((char*)psrc + n) || pdst < psrc) {  
        printf("no-overlap: \n");

        while(cnt1--) *pdst++ = *psrc++;  //4位元組拷貝
        
        byte1 = (char*)pdst;
        byte2 = (char*)psrc;
        while(cnt2--) *byte1++ = *byte2++;  //單位元組拷貝
    }
    else {
        printf("overlap: \n");

        byte1 = (char*)pdst + n - 1;
        byte2 = (char*)psrc + n - 1;
        while(cnt2--) *byte1-- = *byte2--;  //單位元組拷貝

        byte1++, byte2++;
        pdst = (int*)byte1 - 1;
        psrc = (int*)byte2 - 1;
        while(cnt1--) *pdst-- = *psrc--;   //4位元組拷貝
    }

    return dest;
}

char* my_strcpy(char* dest, const char* src)
{
    if(dest == NULL || src == NULL) return NULL;

    char *psrc = (char*)src;
    int n = 0;
    while(*psrc) {  //得到長度
        n++;
        psrc++;
    }

    my_memcpy_byte(dest, src, n);

    return dest;
}

int main()
{
    char a[] = "12345678900000000000000000";
    char b[] = "12345678900000000000000000";
    char buf[100] = {'0'};
    char *p = a + 3;
    char *p1 = b + 10;

    printf("%s\n", my_memcpy(p, a, 7));  //overlap: p所指位于a[0]~a[6]的範圍内
    printf("%s\n", my_memcpy(p1, b, 7)); //p1所指不在b[0]~b[6]的範圍内, 即無重疊
    printf("%s\n", my_strcpy(buf, a));  //獨立的空間, 無重疊

    return 0;
}
           

[程式結果及分析]

動手實作自己的memcpy和strcpy一、問題由來二、動手實作你的memcpy三、動手實作你的strcpy四、測試結果