memcpy函数的用法以及实现一个memcpy函数
- 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?》
memcpy函数的用法以及实现一个memcpy函数的更多相关文章
- memset函数及其用法,C语言memset函数详解
在前面不止一次说过,定义变量时一定要进行初始化,尤其是数组和结构体这种占用内存大的数据结构.在使用数组的时候经常因为没有初始化而产生“烫烫烫烫烫烫”这样的野值,俗称“乱码”. 每种类型的变量都有各自的 ...
- np.random.random()函数 参数用法以及numpy.random系列函数大全
原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/9751471.html 1.np.random.random()函数参数 np.random.r ...
- 【转】np.random.random()函数 参数用法以及numpy.random系列函数大全
转自:https://www.cnblogs.com/DOMLX/p/9751471.html 1.np.random.random()函数参数 np.random.random((1000, 20) ...
- MySQL CAST与CONVERT 函数的用法
MySQL CAST与CONVERT 函数的用法 产生另一个类型的值 MySQL 的CAST()和CONVERT()函数可用来获取一个类型的值,并产生另一个类型的值. 两者具体的语法如下:1 CAS ...
- arguments.callee 调用函数自身用法----JSON.parse()和JSON.stringify()前端js数据转换json格式
arguments.callee 调用函数自身用法 arguments.callee 在哪一个函数中运行,它就代表哪个函数. 一般用在匿名函数中. 在匿名函数中有时会需要自己调用自己,但是由于是匿名函 ...
- SQL decode 函数的用法
decode 函数基本语法: decode(字段|表达式,条件1,结果1,条件2,结果2,...,条件n,结果n,缺省值): --缺省值可以省略 表示如果 字段|表达式 等于 条件1 时,DECODE ...
- python--函数式编程 (高阶函数(map , reduce ,filter,sorted),匿名函数(lambda))
1.1函数式编程 面向过程编程:我们通过把大段代码拆成函数,通过一层一层的函数,可以把复杂的任务分解成简单的任务,这种一步一步的分解可以称之为面向过程的程序设计.函数就是面向过程的程序设计的基本单元. ...
- (60)Wangdao.com第十天_JavaScript 函数_作用域_闭包_IIFE_回调函数_eval
函数 实现特定功能的 n 条语句封装体. 1. 创建一个函数对象 var myFunc = new Function(); // typeof myFunc 将会打印 function ...
- Delphi另一个多线程函数:BeginThread用法
Delphi另一个多线程函数:BeginThread━━━━━━━━━━━━━━━━━━━━━━━━━━ Delphi也提供了一个相同功能的类似函数:function BeginThread( ...
随机推荐
- JPagination分页插件的使用(ASP.NET中使用)
前台代码 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Index.asp ...
- WOSA XFS 官方文档地址
WOSA XFS 官方文档地址: ftp://ftp.cencenelec.eu/CWA/CEN/WS-XFS/
- 洛谷 P3865 ST表
ST表 ST表的功能很简单 它是解决RMQ问题(区间最值问题)的一种强有力的工具 它可以做到O(nlogn)预处理,O(1)查询最值 是一种处理静态区间可重复计算问题的数据结构,一般也就求求最大最小值 ...
- mysql执行拉链表操作
拉链表需求: 1.数据量比较大 2.变化的比例和频率比较小,例如客户的住址信息,联系方式等,比如有1千万的用户数据,每天全量存储会存储很多不变的信息,对存储也是浪费,因此可以使用拉链表的算法来节省存储 ...
- CMakeLists.txt中使用循环
编译一个安卓下的so,此so依赖其他的库,通过循环简化操作 set(UVC_LIBS UVCCamera uvc usb100 jpeg-turbo1500) FOREACH(UVC_LIB ${UV ...
- 6.824 Lab 3: Fault-tolerant Key/Value Service 3A
6.824 Lab 3: Fault-tolerant Key/Value Service Due Part A: Mar 13 23:59 Due Part B: Apr 10 23:59 Intr ...
- Babel编译:类继承
编译前 // 父类 class Fruit { static nutrition = "vitamin" static plant() { console.log('种果树'); ...
- 【ABAP系列】SAP 如何用ABAP实现自动发送外部邮件
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP 如何用ABAP实现自动发 ...
- java中的命名规则
转载自:http://growstep.diandian.com/post/2011-08-17/3989094 1.类名首字母应该大写.属性(成员变量).方法.对象变量以及所有标识符(如形式参数.实 ...
- Android View的Adapter
1 Adapter适配的对象是View Adapter通过为View提供指定格式的数据来适配View,让View可以以事先约定好的方式将内容展示给用户. 所以,进行UI设计的关键是搞清楚各个View组 ...