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

god23bin發表於2021-04-23

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. sourcedestination不能有有任何的重疊。

實現

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;
}

相關文章