天天看点

C的memset,memcpy,strcpy 的区别 及memset memcpy memmove源码

extern void *memcpy(void *dest,void *src,unsigned int count);

#include <string.h>

   功能:由src所指内存区域复制count个字符串到dest所指内存区域.

   说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针.

   memset

   extern void *memset(void *buffer,int c,int count);

   #include <string.h>

   功能:把buffer所指内存区域的前count个字节设置成字符c

   说明:返回指向buffer的指针.

二.区别

    memset 用来对一段内存空间全部设置为某个字符,一般用于在对定义的字符串初始化为' '或者'\0';

    例: char a[100];

          memset(a,'\0',sizeof(a));

   memcpy 是用来做内存拷贝,可以用来拷贝任何数据类型的对象,可以指定拷贝的数据长度;

   例:

           char a[100],b[50];

           memcpy(b,a,sizeof(b));   //注意:如果使用sizeof(a),会造成内存溢出

   mem是一段内存,他的长度,必须自己记住.memcpy是见着什么拷贝什么。

    strcpy 就只能拷贝字符串,它遇到'\0'就结束拷贝;

      例:char a[100],b[50];

              strcpy(a,b);

              如用strcpy(b,a)要注意a中的字符串长度(第一个'\0'之前) 是否超过50,如果超过,则会造成b的

内存溢出.它是不会拷贝'\0'的,所以一般还有加一个语句:

              *a='\0';

三.使用技巧

      memset 可以方便的清空一个数据结构的变量或数组.

      如:

        struct sample_struct

       {

              char csName[16];

              int iSeq;

              int iType;

       };

       对于变量

       struct sample_struct stTest;

       一般情况下,初始化stTest的方法:

        stTest.csName[0]='\0';

        stTest.iSeq=0;

        stTest.iType=0;

    而用memset:

       memset(&stTest,0,sizeof(struct sample_struct));

    如果是数组:

    struct sample_struct TEST[100];

    memset(TEST,0,sizeof(struct sample_struct)*100);

strcpy是拷贝字符串,以\0为标志结束(即一旦遇到数据值为0的内存地址拷贝过程即停止) 

strcpy的原型为 

char *strcpy(char *dest, const char *src) 

而memcpy是给定来源和目标后,拷贝指定大小n的内存数据,而不管拷贝的内容是什么(不仅限于字符) 

memcpy的原型为 

void *memcpy(void *dest, const void *src, size_t n);

memcpy源码:

/***
*memcpy.c - contains memcpy routine
 
*Purpose:
*       memcpy() copies a source memory buffer to a destination buffer.
*       Overlapping buffers are not treated specially, so propogation may occur.
*
 **********/

#include <cruntime.h>
#include <string.h>

#pragma function(memcpy)

/***
*memcpy - Copy source buffer to destination buffer
*
*Purpose:
*       memcpy() copies a source memory buffer to a destination memory buffer.
*       This routine does NOT recognize overlapping buffers, and thus can lead
*       to propogation.
*
*       For cases where propogation must be avoided, memmove() must be used.
*
*Entry:
*       void *dst = pointer to destination buffer
*       const void *src = pointer to source buffer
*       size_t count = number of bytes to copy
*
*Exit:
*       Returns a pointer to the destination buffer
*
*Exceptions:
*******************************************************************************/

void * __cdecl memcpy (
        void * dst,
        const void * src,
        size_t count
        )
{
        void * ret = dst;

#if defined (_M_IA64)

        {


        __declspec(dllimport)


        void RtlCopyMemory( void *, const void *, size_t count );

        RtlCopyMemory( dst, src, count );

        }

#else  /* defined (_M_IA64) */
        /*
         * copy from lower addresses to higher addresses
         */
        while (count--) {
                *(char *)dst = *(char *)src;
                dst = (char *)dst + 1;
                src = (char *)src + 1;
        }
#endif  /* defined (_M_IA64) */

        return(ret);
}      

实现很简单,count一直减,把src值赋给dest,唯一值得注意的是函数原型。

void * __cdecl memcpy (
        void * dst,
        const void * src,
        size_t count
        )

可以看到参数为void 类型的指针,也返回一个void *指针。
我们可以利用void *类型指针为任何类型实现copy,如结构体:      
struct A{
        int x;
        int y;
    };
    A *a=new A();
    A *b=new A();
    a->x=5;
    a->y=3;
     
    memcpy(b,a,sizeof(A));
    printf("%d%d\n",b->x,b->y);      

输出53。

memset源码:

/***
*char *memset(dst, val, count) - sets "count" bytes at "dst" to "val"
*
*Purpose:
*       Sets the first "count" bytes of the memory starting
*       at "dst" to the character value "val".
*
*Entry:
*       void *dst - pointer to memory to fill with val
*       int val   - value to put in dst bytes
*       size_t count - number of bytes of dst to fill
*
*Exit:
*       returns dst, with filled bytes
*
*Exceptions:
*
*******************************************************************************/

void * __cdecl memset (
        void *dst,
        int val,
        size_t count
        )
{
        void *start = dst;

#if defined (_M_IA64) || defined (_M_AMD64)

        {


        __declspec(dllimport)


        void RtlFillMemory( void *, size_t count, char );

        RtlFillMemory( dst, count, (char)val );

        }

#else  /* defined (_M_IA64) || defined (_M_AMD64) */
        while (count--) {
                *(char *)dst = (char)val;
                dst = (char *)dst + 1;
        }
#endif  /* defined (_M_IA64) || defined (_M_AMD64) */

        return(start);
}      

memmove和memcpy

1.memmove

函数原型:void *memmove(void *dest, const void *source, size_t count)

返回值说明:返回指向dest的void *指针

参数说明:dest,source分别为目标串和源串的首地址。count为要移动的字符的个数

函数说明:memmove用于从source拷贝count个字符到dest,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中。

2.memcpy

函数原型:void *memcpy(void *dest, const void *source, size_t count);

函数说明:memcpy功能和memmove相同,但是memcpy中dest和source中的区域不能重叠,否则会出现未知结果。

3.两者区别

  函数memcpy()   从source  指向的区域向dest指向的区域复制count个字符,如果两数组重叠,不定义该函数的行为。   

  而memmove(),如果两函数重叠,赋值仍正确进行。

  memcpy函数假设要复制的内存区域不存在重叠,如果你能确保你进行复制操作的的内存区域没有任何重叠,可以直接用memcpy;   

  如果你不能保证是否有重叠,为了确保复制的正确性,你必须用memmove。

 memcpy的效率会比memmove高一些,如果还不明白的话可以看一些两者的实现:

C的memset,memcpy,strcpy 的区别 及memset memcpy memmove源码
C的memset,memcpy,strcpy 的区别 及memset memcpy memmove源码

void *memmove(void *dest, const void *source, size_t count)

{

 assert((NULL != dest) && (NULL != source));

 char *tmp_source, *tmp_dest;

 tmp_source = (char *)source;

 tmp_dest = (char *)dest;

 if((dest + count<source) || (source + count) <dest))

 {// 如果没有重叠区域

   while(count--)

     *tmp_dest++ = *tmp_source++;

}

else

{ //如果有重叠

 tmp_source += count - 1;

 tmp_dest += count - 1;

 while(count--)

   *--tmp_dest = *--tmp;

return dest;

C的memset,memcpy,strcpy 的区别 及memset memcpy memmove源码
C的memset,memcpy,strcpy 的区别 及memset memcpy memmove源码

void *memcpy(void *dest, const void *source, size_t count)

 char *tmp_dest = (char *)dest;

 char *tmp_source = (char *)source;

 while(count --)//不对是否存在重叠区域进行判断

   *tmp_dest ++ = *tmp_source ++;

 return dest;

memmove普通实现

memmove - Copy source buffer to destination buffer

;

;Purpose:

;       memmove() copies a source memory buffer to a destination memory buffer.

;       This routine recognize overlapping buffers to avoid propogation.

;       For cases where propogation is not a problem, memcpy() can be used.

;   Algorithm:

    void* memmove(void* dest, void* source, size_t count)

   {

       void* ret = dest;

       if (dest <= source || dest >= (source + count))

       {

          //Non-Overlapping Buffers

         //copy from lower addresses to higher addresses

         while (count --)

               *dest++ = *source++;

     }

     else

     {

        //Overlapping Buffers

       //copy from higher addresses to lower addresses

       dest += count - 1;

       source += count - 1;

       while (count--)

                *dest-- = *source--;l

      }

      return ret;

   }

另一种实现:

void* mymemcpy( void* dest, const void* src, size_t count )

    char* d = (char*)dest;

    const char* s = (const char*)src;

  //  int n = (count + 7) / 8; // count > 0 assumed

    int n = count >> 3;

    switch( count & 7 )

    {

              do {  *d++ = *s++;

    case 7:        *d++ = *s++;

    case 6:        *d++ = *s++;

    case 5:        *d++ = *s++;

    case 4:        *d++ = *s++;

    case 3:        *d++ = *s++;

    case 2:        *d++ = *s++;

    case 1:        *d++ = *s++;

    case 0          } //while (--n > 0);

                 while (n-- > 0)

    }

    return dest;

C的memset,memcpy,strcpy 的区别 及memset memcpy memmove源码
C的memset,memcpy,strcpy 的区别 及memset memcpy memmove源码
C的memset,memcpy,strcpy 的区别 及memset memcpy memmove源码