• 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;
}

这里还是考虑了写覆盖的代码,但是写覆盖的代码和之前一个字节一个字节拷贝的实现方式是一样的。

memcpy函数的用法以及实现一个memcpy函数的更多相关文章

  1. memset函数及其用法,C语言memset函数详解

    在前面不止一次说过,定义变量时一定要进行初始化,尤其是数组和结构体这种占用内存大的数据结构.在使用数组的时候经常因为没有初始化而产生“烫烫烫烫烫烫”这样的野值,俗称“乱码”. 每种类型的变量都有各自的 ...

  2. np.random.random()函数 参数用法以及numpy.random系列函数大全

    原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/9751471.html 1.np.random.random()函数参数 np.random.r ...

  3. 【转】np.random.random()函数 参数用法以及numpy.random系列函数大全

    转自:https://www.cnblogs.com/DOMLX/p/9751471.html 1.np.random.random()函数参数 np.random.random((1000, 20) ...

  4. MySQL CAST与CONVERT 函数的用法

    MySQL CAST与CONVERT 函数的用法 产生另一个类型的值  MySQL 的CAST()和CONVERT()函数可用来获取一个类型的值,并产生另一个类型的值. 两者具体的语法如下:1 CAS ...

  5. arguments.callee 调用函数自身用法----JSON.parse()和JSON.stringify()前端js数据转换json格式

    arguments.callee 调用函数自身用法 arguments.callee 在哪一个函数中运行,它就代表哪个函数. 一般用在匿名函数中. 在匿名函数中有时会需要自己调用自己,但是由于是匿名函 ...

  6. SQL decode 函数的用法

    decode 函数基本语法: decode(字段|表达式,条件1,结果1,条件2,结果2,...,条件n,结果n,缺省值): --缺省值可以省略 表示如果 字段|表达式 等于 条件1 时,DECODE ...

  7. python--函数式编程 (高阶函数(map , reduce ,filter,sorted),匿名函数(lambda))

    1.1函数式编程 面向过程编程:我们通过把大段代码拆成函数,通过一层一层的函数,可以把复杂的任务分解成简单的任务,这种一步一步的分解可以称之为面向过程的程序设计.函数就是面向过程的程序设计的基本单元. ...

  8. (60)Wangdao.com第十天_JavaScript 函数_作用域_闭包_IIFE_回调函数_eval

    函数        实现特定功能的 n 条语句封装体. 1. 创建一个函数对象 var myFunc = new Function(); // typeof myFunc 将会打印 function ...

  9. Delphi另一个多线程函数:BeginThread用法

    Delphi另一个多线程函数:BeginThread━━━━━━━━━━━━━━━━━━━━━━━━━━ Delphi也提供了一个相同功能的类似函数:function BeginThread(    ...

随机推荐

  1. Spring Security 报There is no PasswordEncoder mapped for the id "null"

    查了下发现是spring security 版本在5.0后就要加个PasswordEncoder了 解决办法 在securityConfig类下加入NoOpPasswordEncoder,不过官方已经 ...

  2. open jdk卸载

    //查找:open jdk # rpm -qa | grep java //卸载open jdk # rpm -e --nodeps 包 # source /etc/profile # java -v ...

  3. 一、基础篇--1.1Java基础-包装类的装箱和拆箱

    包装类:java是典型的面向对象编程,但是八种基本数据类型并不支持面向对象编程.基本类型的数据不具备对象的特性,没有属性和方法.沿用它们只是为了迎合人类根深蒂固的习惯,并的确能简单.有效地进行常规数据 ...

  4. Openstack_通用模块_Oslo_vmware 创建 vSS PortGroup

    目录 目录 vSS vSSPG vSphere SDK 中相关的网络对象 创建 vSS PortGroup vSS & vSSPG vSS(Standard vSwitch 标准交换机) 为在 ...

  5. Nginx1.8源码包编译安装

    1.下载解压Nginx,为方便管理下载包一般将下载包放在指定目录下 ,即/usr/local/src/下. wget http://nginx.org/download/nginx-1.8.0.tar ...

  6. 使用cesium中的scene.open中遇到的几个问题

    有些服务是发在场景(scene)下的,超图提供了一个很方便的方法:scene.open,这个方法会将场景中所有的图层(无论是OSGB还是影像和地形)加载进来.同时这个方法会自带一个自动地位功能,具体实 ...

  7. 【java】的传值方式

    [java]的传值方式 当你问大多数程序员Java是传值还是传引用的时候,你可能会得到两种答案之一: (1)Java传递原始类型数据时使用的是传值方式:传递对象时则使用传引用方式:String类型的数 ...

  8. Java ——补充:构造方法 super()与构造方法 无参 有参构造方法 this()与构造方法

    参考文章: https://blog.csdn.net/qq_33322074/article/details/86030836 https://blog.csdn.net/HD243608836/a ...

  9. cocos2dx基础篇(16) 基本绘图DrawPrimitives

    [3.x] (1)去掉前缀 "cc" (2)将 ccDraw***() 封装到了 DrawPrimitives 命名空间中. (3)重写绘图函数:         draw(Ren ...

  10. linux/linux学习笔记-vim文本编辑器(mooc)

    vim文本编辑器 vim与vi的区别:( vim=vi +IMproved) VIM是一个Unix以及类unix文本编辑器 特点:功能强大,高度可定制 vim编辑器的三种模式:一般模式.编辑模式和命令 ...