手寫strcpy和memcpy代碼實現

本篇文章聊一下strcpy和memcpy的代碼實現,這兩個也是c和c++面試中常考的問題點。c++

1. 手寫strcpy

首先看一下,一份標準的strcpy的實現以下:面試

char *strcpy(char* strDest, const char* strSrc)
{
    assert( (strDest != NULL) && (strSrc != NULL));
    char *address = strDest;
    while((*strDest++ = *strSrc++) != '\0');
    return address;
}

這裏有以下幾點須要注意:函數

  • 源字符串要使用const類型,避免在程序中被修改;
  • 在函數入口處要加上斷言,檢查源字符串指針和目標字符串指針是否有爲空的,不然會產生不可預料的錯誤;
  • 使用while循環要簡單而清晰,使用盡可能精簡的代碼;
  • 返回值要是char*,且返回了目標字符串指針的原值,使得函數能夠支持鏈式表達式,增長了函數的附加值。

以上這些點不僅適用於strcpy這裏,而是咱們任什麼時候候寫代碼都儘可能遵循這些規則,這樣才能寫出高可用、高健壯性的代碼。this

對於以上代碼,咱們能夠看出來,它是存在隱患的,當源字符串的長度超出目標字符串時,會致使把數據寫入到咱們沒法控制的地址中去,存在很大的風險,因此就有了strncpy,下面也給一個strncpy的實現,以下:spa

char *strncpy(char* strDest, const char* strSrc, size_t n)
{
    assert( (strDest != NULL) && (strSrc != NULL));
    char *address = strDest;
    while ( n-- && (*strDest++ = *strSrc++) != '\0');
    return address;
}

要根據輸入的長度作一個控制,而每每咱們使用的時候,輸入的長度都是目標字符串的長度減1,由於要留一個位置給結束符'\0'指針

2. memcpy的實現

memcpy的實現其實能夠參考strncpy的實現,好比咱們把指針類型轉換成char*來實現拷貝,這種方式就是按照一個字節一個字節來進行拷貝了,首先仍是一睹代碼爲快,以下:code

#include <stdio.h>
#include <string.h>

struct people
{
    int iAge;
    char szName[12];
    char szSex[3];
};

//模擬memcpy的實現
void * mymemcpy(void *dest, const void *src, size_t n)
{
    if (dest == NULL || src == NULL)
          return NULL;
    char *pDest = static_cast <char*>(dest);
    const char *pSrc  = static_cast <const char*>(src);
    if (pDest > pSrc && pDest < pSrc+n)
    {
        for (size_t i=n-1; i != -1; --i)
        {
                pDest[i] = pSrc[i];
        }
    }
    else
    {
        for (size_t i= 0; i < n; i++)
        {
                pDest[i] = pSrc[i];
        }
    }

    return dest;
}

int main()
{
    people stPeople1, stPeople2;
    memset((void*)&stPeople1, 0x00, sizeof(stPeople1));
    memset((void*)&stPeople2, 0x00, sizeof(stPeople2));
    stPeople1.iAge = 32;
    mymemcpy(stPeople1.szName, "li lei", sizeof(stPeople1.szName));
    mymemcpy(stPeople1.szSex, "man", sizeof(stPeople1.szSex));

    mymemcpy((void*)&stPeople2, (void*)&stPeople1, sizeof(stPeople2));
    
    printf("this people age is %d, name is %s, sex is %s\n", stPeople2.iAge, stPeople2.szName, stPeople2.szSex);
    
    return 0;
}

咱們看mymemcpy的實現,此時是一個字節的實現,但它與strncpy實現不能同樣,看一下memcpy實現的注意點:內存

  • 一樣的,在函數入口處要檢查源字符串指針和目標字符串指針是否有爲空的,不然會產生不可預料的錯誤;
  • 由於是按照一個字節拷貝,那就要把形參轉換成char*類型來操做;
  • 要檢查源內存和目標內存是否存在內存重疊,若是目標內存首地址在源內存的中間,則要從後往前拷貝,由於若是從前日後拷貝,那從目標內存首地址開始的地方就會被覆蓋掉,若是沒有重疊,或者源內存地址在目標內存的中間,那沒有關係,能夠從前日後拷貝;
  • 不能使用'\0'來判斷拷貝的結束,由於它是對一整塊內存的拷貝,舉一個淺顯的例子,假設拷貝一個結構體,相似上面代碼,那麼它極可能拷貝到中間的某個地方就中止了,這個拷貝就至關於沒有完成;
  • 一樣的,memcpy也要返回目標字符串地址;

可是這個時候若是面試官問你,按照4個字節來拷貝,該怎麼實現呢?字符串

根據前面的邏輯,其實按照4個字節拷貝就是把指針類型轉換成int*來拷貝,不過有一點不一樣的是,若是按照4個字節來拷貝,就沒辦法去判斷內存重疊的狀況了,由於內存的基本單位是一個字節,4個字節是沒有辦法避免覆蓋狀況的,一個可用的4個字節拷貝代碼以下:string

void * mymemcpy(void *dest, const void *src, size_t n)
{
    if (dest == NULL || src == NULL)
          return NULL;
    int *pDest = (int*)dest;
    int *pSrc  = (int*)src;
    int iWordNum = n/4;
    int iSlice = n%4;
    while(iWordNum--)
    {
        *pDest++ = *pSrc++;
    }
    char* pcDest = (char*)pDest;
    char* pcSrc = (char*)pSrc;

    while(iSlice--)
    {
        *pcDest++ = *pcSrc++;
    }

    return dest;
}

按照4個字節判斷,一個清晰的思路是知足4個字節的按照int類型去拷貝,不知足4個字節的就仍是按照char類型去拷貝。

好了,關於strcpy和memcpy的實現就介紹到這裏,若是個人創做對你有用的話,麻煩點個讚唄。

相關文章
相關標籤/搜索