- 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?》