memcpy函数

原型:extern void *memcpy(void *dest, const void *src,size_t n); 这是一个函数指针 接收的是一个地址 dest是接收地址的首地址,src是源首地址,count是接收目标的大小单位为字节 位于cstring或memcpy.h头文件中。

Linux下memcpy源码实现

void *memcpy(void *to, const void *from, size_t n)
{
    void *xto = to;
    size_t temp, temp1;
 
    if (!n)
        return xto;
    if ((long)to & 1) {
        char *cto = to;
        const char *cfrom = from;
        *cto++ = *cfrom++;
        to = cto;
        from = cfrom;
        n--;
    }
    if (n > 2 && (long)to & 2) {
        short *sto = to;
        const short *sfrom = from;
        *sto++ = *sfrom++;
        to = sto;
        from = sfrom;
        n -= 2;
    }
    temp = n >> 2;
    if (temp) {
        long *lto = to;
        const long *lfrom = from;
#if defined(CONFIG_M68000) || defined(CONFIG_COLDFIRE)
        for (; temp; temp--)
            *lto++ = *lfrom++;
#else
        asm volatile (
            "    movel %2,%3\n"
            "    andw  #7,%3\n"
            "    lsrl  #3,%2\n"
            "    negw  %3\n"
            "    jmp   %%pc@(1f,%3:w:2)\n"
            "4:    movel %0@+,%1@+\n"
            "    movel %0@+,%1@+\n"
            "    movel %0@+,%1@+\n"
            "    movel %0@+,%1@+\n"
            "    movel %0@+,%1@+\n"
            "    movel %0@+,%1@+\n"
            "    movel %0@+,%1@+\n"
            "    movel %0@+,%1@+\n"
            "1:    dbra  %2,4b\n"
            "    clrw  %2\n"
            "    subql #1,%2\n"
            "    jpl   4b"
            : "=a" (lfrom), "=a" (lto), "=d" (temp), "=&d" (temp1)
            : "0" (lfrom), "1" (lto), "2" (temp));
#endif
        to = lto;
        from = lfrom;
    }
    if (n & 2) {
        short *sto = to;
        const short *sfrom = from;
        *sto++ = *sfrom++;
        to = sto;
        from = sfrom;
    }
    if (n & 1) {
        char *cto = to;
        const char *cfrom = from;
        *cto = *cfrom;
    }
    return xto;
}

windows下的实现

void* __cdecl memcpy(void* dst,const void* src,size_t count)
{
    void*ret=dst;
{
     extern void RtlMoveMemory(void *,const void *,size_t count);
     RtlMoveMemory(dst,src,count);
}
while(count--){
    *(char *)dst = *(char *)src;
    dst = (char *)dst+1;
    src = (char *)src+1;
}
return (ret);
} 
//改进之后的memcpy

   void *my_memcpy_byte(void *dst, const void *src, int n)
{
  if (dst == NULL || src == NULL || n <= 0)
      return NULL;

  char * pdst = (char *)dst;
  char * psrc = (char *)src;

  if (pdst > psrc && pdst < psrc + n)
  {
      pdst = pdst + n - 1;
      psrc = psrc + n - 1;
      while (n--)
          *pdst-- = *psrc--;
  }
  else
  {
      while (n--)
          *pdst++ = *psrc++;
  }
  return dst;
}

使用注意事项

  • src地址是否为空
  • src和dest地址是否重合,是安全重合还是部分重合
  • 当处理大块数据的时候可能存在效率低下的问题。

memcpy_s函数

函数 memcpy_s( void *dest, size_t numberOfElements, const void *src, size_t count )

函数原型

errno_t memcpy_s(
   void *dest,
   size_t numberOfElements,
   const void *src,
   size_t count 
);

dest:目标的地址
numberOfElements:目标的size
src:源地址
count:要拷贝的字节数
先申请了一个缓冲区大小,为numberofelements,当count超过这个缓冲区的时候就开始溢出

memcpy_s详解

第一个参数为目标内存地址,第二个参数为目标内存缓冲大小,第三个参数为源内存地址,第四个为源内存缓冲的大小。返回值是一个错误码。
为什么这个返回值是错误码呢?因为这个版本中加入了基本的错误检测。如果源缓冲大小为0,即count为0,函数返回0,什么也不做。此函数没有对目标指针为NULL的情况,不做检查,所以你自己要注意检查。如果指针有值,但是是无效值,函数也没办法检查是否是有效内存,只是会搜集这些信息,在程序崩溃时提供调试需要的信息。
然后检查源地址是否为空或count是否大于sizeInBytes,两个条件有一个满足,函数中先将目标内存以sizeInBytes指定的大小调用memset函数清0.这里可以看出,如果sizeInBytes传入的大小超出目标缓冲区的大小,也是会带来隐患的,一旦清除了其他进程的或者其他线程的内存,都是带来问题,也很可能导致内存操作违规。所以,第二个参数的大小不能超过目标缓冲的大小,以字节为单位。然后程序搜集错误信息,最后返回错误码,并不会执行内存复制的过程。前面说的这是两个条件任意一个进入都会导致失败,所以在搜集信息时,程序会对进入的条件进行判断,然后进行搜集。
只有这些检查通过,才会调用memcpy函数执行内存复制过程。而第二个参数只是内部用来检测和清除目标内存而使用的。
函数memcpy_s执行完毕后返回0,所以检查返回值是否为0不能判断是否成功。但是返回值为非零那就是失败了。
虽然说加上_s版本的函数是安全版本,但是也是会出现问题的,使用时也要注意,即使有时候不崩溃,但是一旦改写了其他的线程的内存,必然导致程序运行不正常,因为线程的数据被破坏,至少逻辑会出错。
不过,_s版本函数提供了一定的检测,并且在release版本也可以报错,可以进一步调试问题,而memcpy则在release版本中不会报错

本文参考:C++的Memcpy与Memcpy_s函数解析

Logo

更多推荐