C語言-記憶體函數的實現(一)之memcpy

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

  • memcpy
  • memmove
  • memcmp
  • memset

下面看看memcpy函數

memcpy

我們想想,之前有那個字元串拷貝的函數,即strcpy函數。都有拷貝的函數了,為什麼還要這個記憶體拷貝函數呢?

C語言-字元串函數的實現(二)之strcpy

不能直接用strcpy嗎?這是一個好問題,那下面就試試它。

我們準備兩個整型數組,分別為arr1和arr2,然後通過strcpy函數把arr1的內容拷貝到arr2中,程式碼如下

int main() 
{
    int arr1[] = { 1,2,3,4,5 };
    int arr2[5] = { 0 };
    // 把arr1的內容拷貝到arr2中
    strcpy(arr2, arr1);
    return 0;
}

那麼這裡肯定會有警告,因為類型不同。

直接運行,通過Debug,看記憶體,可以發現實現不了完整的拷貝,strcpy只拷貝了一個位元組

顯然,strcpy函數不適用於其他類型的數據拷貝,所以呢,就出現記憶體拷貝了,使任意類型的數據都能進行拷貝。

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

memcpy文檔

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

Copy block of memory

拷貝記憶體塊(拷貝記憶體數據)

Copies the values of num bytes from the location pointed to by source directly to the memory block pointed to by destination.

從source(源記憶體塊位置)直接指向的地方開始複製num個位元組的數據到destination指向的記憶體塊位置。

The underlying type of the objects pointed to 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 to by both the destination and source parameters, shall be at least num bytes, and should not overlap (for overlapping memory blocks, memmove is a safer approach).

為了避免溢出,這兩個數組的大小至少為num個位元組,而且這兩個數組記憶體位置不應該重疊。

我們從文檔中可以看出

  1. 參數中除了要複製的位元組數num,其他的參數類型基本都是void*,返回值也是void*
  2. 該函數是從source的位置開始向後複製num個位元組的數據到destination的記憶體位置。
  3. 該函數在遇到 '\0' 的時候並不會停下來。
  4. source和destination不能有有任何的重疊。

實現

void* 不能直接解引用,那麼如何複製呢?答案是進行類型強轉,轉換成char*,一個一個位元組地複製過去。

一開始得先搞一個指針變數*rest來存儲dest,不然目的地址後面發生改變就不知道地址是哪裡了。接下來就是主要操作了,把src指向的數據複製給dest,即*(char*)dest = *(char*)src,然後這兩個指針都進行偏移,往後走,繼續複製,就是循環下去了,直到num個位元組複製完就結束,返回目的地址rest。程式碼如下

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

void* my_memcpy(void* dest, const void* src, size_t num) 
{
    assert(dest != NULL);
    assert(src != NULL);
    void* rest = dest;
    // void* 不能直接解引用,那麼如何複製呢?
    // 給了num個位元組,也就是需要複製num個位元組
    // 那就轉換成char*,一個一個位元組的複製過去
    while (num--) 
    {
        *(char*)dest = *(char*)src;
        //++(char*)dest;
        //++(char*)src;
        ((char*)dest)++;
        ((char*)src)++;
    }
    return rest;
}

以上,就是memcpy函數的實現。

測試程式碼

我們就使用下面的測試程式碼,測試下自己實現的my_memcpy函數,通過Debug來觀察記憶體數據的變化。

struct S
{
    char name[20];
    int age;
};

int main() 
{
    int arr1[] = { 1,2,3,4,5 };
    int arr2[5] = { 0 };

    my_memcpy(arr2, arr1, sizeof(arr1));

    struct S arr3[] = { {"LeBron", 36}, {"Kobe", 41} };
    struct S arr4[3] = { 0 };

    my_memcpy(arr4, arr3, sizeof(arr3));

    return 0;
}