memcpy函数实现及其优化

电子常识

2641人已加入

描述

  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)。

  实现1:《高质量c++,c编程指南》

  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

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分