memcpy函數實現及其優化
1:函數原型void * memcpy ( void * destination, const void * source, size_t num );
函數作用
參考:http://www.cplusplus.com/reference/clibrary/cstring/memcpy/
Copy block of memory
Copies the values of num bytes from the location pointed by source directly to the memory block pointed by destination.
The underlying type of the objects pointed 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.
To avoid overflows, the size of the arrays pointed by both the destination and source parameters, shall be at least numbytes, and should not overlap (for overlapping memory blocks, memmove is a safer approach)。
void *mymemcpy(void *dst,const void *src,size_t num)
{
assert((dst!=NULL)&&(src!=NULL));
//assert(des》=src+num||src》dst+num);
byte * psrc = (byte *)src;//byte 既為unsigned char類型
byte * pdst = (byte *)dst;
while(num--》0)*pdst++ = *psrc++;
return dst;
}
缺點:沒有考慮內存重疊的情況,可以加一個斷言換為:assert(des》=src+num||src》dst+num);
實現2:考慮重疊,有重疊情況也復制
void * mymemcpy(void *dest, const void *src, size_t count)
{
if (dest == NULL || src == NULL)return NULL;
char *pdest = static_cast 《char*》(dest);
const char *psrc = static_cast 《const char*》(psrc);
int n = count;
if (pdest 》 psrc && pdest 《 psrc+count)
{
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;
}
對memcpy函數的改進:
改進思想:
大部分認為memcpy是一個char到char的拷貝的循環,擔心它的效率。實際上,memcpy是一個效率最高的內存拷貝函數,他不會那么傻,來做一個一個字節的內存拷貝,在地址不對齊的情況下,他是一個字節一個字節的拷,地址對齊以后,就會使用CPU字長來拷(和dma類似),32bit或64bit,還會根據cpu的類型來選擇一些優化的指令來進行拷貝。總的來說,memcpy的實現是和CPU類型、操作系統、cLib相關的。毫無疑問,它是內存拷貝里效率最高的,請放心使用。
1 void *mymemcpy(void *dst,const void *src,size_t num)
2 {
3 assert((dst!=NULL)&&(src!=NULL));
4 int wordnum = num/4;//計算有多少個32位,按4字節拷貝
5 int slice = num%4;//剩余的按字節拷貝
6 int * pintsrc = (int *)src;
7 int * pintdst = (int *)dst;
8 while(wordnum--)*pintdst++ = *pintsrc++;
9 while (slice--)*((char *)pintdst++) =*((char *)pintsrc++);
10 return dst;
11 }
memcpy函數用法詳解
1、memcpy 函數用于 把資源內存(src所指向的內存區域) 拷貝到目標內存(dest所指向的內存區域);拷貝多少個?有一個size變量控制
拷貝的字節數;
函數原型:void *memcpy(void *dest, void *src, unsigned int count);
用法:(1)可以拷貝任何類型的對象,因為函數的參數類型是void*(未定義類型指針),也就是說傳進去的實參可以是int*,short*,char*等等,
但是由于函數拷貝的過程是一個字節一個字節的拷貝的,所以實際操作的時候要把void*強制轉化為char*,這樣在指針加的時候才會保證每次加一個字節,呵呵
函數源代碼實現:
void *memcpy1(void *desc,const void * src,size_t size)
{
if((desc == NULL) && (src == NULL))
{
return NULL;
}
unsigned char *desc1 = (unsigned char*)desc;
unsigned char *src1 = (unsigned char*)src;
while(size-- 》0)
{
*desc1 = *src1;
desc1++;
src1++;
}
return desc;
}
int _tmain(int argc, _TCHAR* argv[])
{
int dest[2] = {0};
const char src[5] = “1234”;
//printf(src);
memcpy1(dest,src,sizeof(src));
//*(dest+5) = ‘/0’;
printf((char *)dest);
int m = -1;
return 0;
}
123456789101112131415161718192021222324252627
注意事項:(1)void* 一定要返回一個值(指針),這個和void不太一樣!
(2)首先要判斷指針的值不能為空,desc為空的話肯定不能拷貝內存空間,src為空相當于沒有拷貝;所以之間return掉;
(3)”“空串是指內容為0,NULL是0,不是串;兩個不等價;
(4)int dest[2] = {0};這是對int 類型的數組初始化的方法;如果是char類型,就用char a[5] = “1234”; 注意數組下標要
多于實際看到的字符數,因為還有’/0’
(5)printf((char *)dest);這句話,是把 char 類型 src 傳到 int 類型的 dest的內存強制轉化成char類型,然后打印出來;
因為直接看int類型的dest是看不到里面的內容的;因為有unsigned char desc1 = (unsigned char)desc;所以字符可以傳
到dest里面保存起來,dest所指向的內存長度4個字節,強制轉化為char 就是把四個字節分成一個一個的字節,這樣就可以看到
一個個字符了,如果定義成char dest[5] = “1234”;就不用轉化;呵呵,表達起來真累人;
(6)memcpy1(dest,src,sizeof(src));注意里面的sizeof(src),這個是包括字符串的結束符’/0’的;所以不用擔心printf(dest);
但是如果用memcpy1(dest,src,4);沒有’/0’就要*(dest+5) = ‘/0’;這樣保證是一個完整的字符串;
(7)如果初始化的時候:
char dest[1024] = “12345666”;//{0};
const char src[5] = “3333”;
那么拷貝的時候,如果用memcpy1(dest,src,sizeof(src));則printf(dest);出來是3333
如果memcpy1(dest,src,4);則printf(dest);出來是33335666;因為上面的sizeof(src),包含’/0’,所以拷貝過去的字符串以’/0’
結束,就只有3333,而如果傳4個字符,’/0’是第五個字符,那就遇到dest[1024] 的’/0’結束,所以是33335666
字符串的’/0’問題一定要注意啊!!!
實際應用:
unsigned char g_pData[1024] = “”;
DWORD g_dwOffset = 0;
bool PackDatatoServer(const unsigned char *pData, const unsigned int uSize)
{
memcpy(g_pData+g_dwOffset, pData, uSize);
g_dwOffset += uSize;
//g_pData += uSize;
return true;
}
void main()
{
const unsigned char a[4] = “123”;
PackDatatoServer(a, 3);
PackDatatoServer(a, 1111);
int b = -1;
}
12345678910111213141516
PackDatatoServer()函數的作用是把每次的資源內存拷貝到目標內存里面,而且是累加的拷貝;也就是后一次緊接著上一次的拷貝;
顯然用到了memcpy函數;
實現原理是用到了一個全局變量g_dwOffset 保存之前拷貝的長度,最開始沒有想到這一點,結果每次拷貝都是一次性的,下一次拷貝把
上一次的沖掉了;所以用全局變量記錄拷貝的長度;
第二個需要注意的是,拷貝的過程中注意不要改變目標指針的指向,即目標指針始終指向初始化的時候指向的位置;那么怎么實現累積拷貝呢?
就是用的指針偏移;第一次實現的時候,把g_pData += uSize;寫到了函數里面,這樣寫是能夠實現指針位移的目標,但是指針指向也發生改變;
另外:g_pData += uSize;也有報錯:left operand must be l-value,原因是:把地址賦值給一個不可更改的指針!
比如:
char a[100];
char *p = new char[10];
a = p; //這里出錯,注意了:數組的首地址也是一個常量指針,指向固定不能亂改的~~
char * const pp = new char[1];
pp = a; //也錯
123456
所以既不能改變首地址,又要滿足累積賦值(就是賦值的時候要從賦過值的地方開始向下一個內存塊賦值,想到指針加),所以想到把指針加寫到
函數參數里面,這時就要充分了解memcpy的實現過程,里面是一個一個字符的賦值的,想連續賦值,就要把指針指向連續的內存的首地址,所以,真的很不好表達就這樣了。
責任編輯:YYX
評論
查看更多