- memcpy的用法
在項目中經常用到memcpy
來實現內存的拷貝工作,如下代碼片段
memcpy( pData, m_pSaveData_C, iSize * sizeof( unsigned short ) );
memcpy
的函數原型為:
void * memcpy ( void * destination, const void * source, size_t num );
memcpy函數的功能是從源內存地址的起始位置開始拷貝若干個字節到目標內存地址中,即從源source中拷貝num個字節到目標destin中。
示例代碼
int main()
{
vector<int> vec;
vector<int> vec1;
vec.push_back(10);
vec.push_back(100);
vec1.resize(vec.size());
memcpy(vec1.data(),vec.data(),vec.size() * sizeof(int));
for (vector<int>::iterator it = vec1.begin();it != vec1.end();it++)
{
cout << *it;
}
char myname[] = "Pierre dee Fermat";
memcpy(person.name,myname,strlen(myname) + 1);
person.age = 46;
cout << person.name << " " << person.age << endl;
cout << "sizeof(person) = " << sizeof(person) << endl;
memcpy(&person_copy,&person, sizeof(person));
cout << person_copy.name << " " << person_copy.age << endl;
return 0;
}
注意:是按照字節拷貝,這個剛開始的時候我總是用錯。剛開始的時候,memcpy(vec1.data(),vec.data(),vec.size() * sizeof(int));
我總是寫成memcpy(vec1.data(),vec.data(),vec.size());
忘記加后面的sizeof(int)
。
另外這個函數不能處理內存重疊的情況,當source和destin所指的內存區域重疊,那么這個函數並不能夠確保source所在重疊區域在拷貝之前不被覆蓋。而使用memmove
可以用來處理重疊區域。函數返回指向destin的指針。
- 實現一個memcpy
1.按字節(Byte)拷貝實現的memcpy
/*
按照字節(Byte)拷貝實現的my_memcpy
*/
void* my_memcpy(void* dst,const void* src,int n)
{
if (dst == NULL || src == NULL || n <= 0)
{
//void* 一定要有返回值 void可以沒有返回值 void*和void不相同
return NULL;
}
char* pdst = (char *)dst;
char* psrc = (char *)src;
if (psrc < pdst && pdst < psrc + n)
{
pdst = pdst + n - 1;
psrc = psrc + n - 1;
while (n--)
{
*pdst = *psrc;
pdst--;
psrc--;
}
}
else
{
while (n--)
{
*pdst = *psrc;
pdst++;
psrc++;
}
}
return dst;
}
上面的按字節拷貝的過程中考慮了拷貝覆蓋,連續的一段空間存放數據是從低地址到高地址進行存放。先從源地址中取出數據,然后寫入到目的地址空間中。目的空間的起始地址如果在源空間之內就會出現內存覆蓋的情況。
如下圖:
這種情況一定要先從尾部拷貝,避免數據覆蓋,不過這種情況也會破壞src空間數據,雖然在src前面加了const關鍵字,表示這部分空間是只讀的,但是編譯器並不會報錯。
測試代碼:
int main()
{
/* 測試內存重疊的情況 */
char str1[] = { "abcdefg" };
my_memcpy(str1 + 2,str1,sizeof(char) * 4);
//memmove(str1 + 2, str1, sizeof(char) * 4);
cout << str1 << endl;
return 0;
}
2.按照4字節拷貝
上面的拷貝是一次拷貝一個字節,現在考慮一次拷貝四個字節,充分的利用總線位寬。
/*
按4字節拷貝
*/
void* my_memcpy1(void* dst,void* src,int n)
{
if (dst == NULL || src == NULL || n <= 0)
{
//void* 一定要有返回值 void可以沒有返回值 void*和void不相同
return NULL;
}
int* pdst = (int*)dst;
int* psrc = (int*)src;
char* temp1 = NULL;
char* temp2 = NULL;
int c1 = n / 4;
int c2 = n % 4;
if (psrc < pdst && pdst < psrc + n)
{
temp1 = (char*)pdst + n - 1;
temp2 = (char*)psrc + n - 1;
while (n--)
{
*temp1 = *temp2;
temp2--;
temp1--;
}
}
else
{
while (c1--)
{
*pdst= *psrc;
pdst++;
psrc++;
}
temp1 = (char*)pdst;
temp2 = (char*)psrc;
while (c2--)
{
*temp1= *temp2;
temp1++;
temp2++;
}
}
return dst;
}
這里還是考慮了寫覆蓋的代碼,但是寫覆蓋的代碼和之前一個字節一個字節拷貝的實現方式是一樣的。
- 參考資料
1 https://www.cnblogs.com/chuanfengzhang/p/8447251.html 《實現memcpy()函數及過程總結》
2 https://www.xuebuyuan.com/778923.html 《閑着沒事測了一下memcpy》
3 https://www.zhihu.com/question/35172305 《怎樣寫出一個更快的 memset/memcpy?》