Wednesday, November 23, 2011

memcpy and memmove

It is interesting to notice the difference between the memcpy and memmove when programming in C. 

While the memcpy copies the data byte-by-byte sequentially from source to destination, the memmove function copies the block of data to the source to the destination. The difference implies that the memcpy function can generate the repeating data results (see below) if the source data being copied happens to be overlapping with the destination data:

#include <stdio.h>
#include <string.h>


int main()
{
    char src1[] = "abcdefg";
    char src2[] = "abcdefg";
    char *dest1;
    char *dest2;
    
    if ((dest1 = (char*)malloc(8*sizeof(char))) == NULL
        || (dest2 = (char*)malloc(8*sizeof(char))) == NULL)
        exit(1);
    
    //memcpy src1 -> dest1
    memcpy(dest1, src1, 7);
    printf("memcpy\tsrc1:%s\tdest1:%s\n", src1, dest1);
    
    //memmove src2 -> dest2
    memmove(dest2, src2, 7);
    printf("memmove\tsrc2:%s\tdest2:%s\n", src2, dest2);
    
    //memcpy src1 -> src1+1
    memcpy(src1+1, src1, 6);
    printf("memcpy\tsrc1:%s\n", src1);
        
    //memmove src2 -> src2+1
    memmove(src2+1, src2, 6);
    printf("memmove\tsrc2:%s\n", src2);
    
    free (dest1);
    free (dest2);
    return 0;
}

The result would be:


memcpy  src1:abcdefg    dest1:abcdefg
memmove src2:abcdefg    dest2:abcdefg
memcpy  src1:aaaaaaa
memmove src2:aabcdef


Thus, memcpy should be used with caution when the data is overlapping with each other.  Specifically, the memmove works identically to memcpy if:
src_addr + length_copy < dest_addr or src_addr > dest_addr 
and it works reversely in the other way. So, the memmove can also be implemented as memmove2:


void * memmove2(void *dest, const void *src, size_t len)
{
    if (src != dest)
    {
        if ((const char*)src+len < dest || (const char*)src > dest)
            memcpy(dest, src, len);
        else
            while (len--)
                *((char*)dest+len) = *((const char*)src+len);
    }
    return dest;
}

If we compare the following code snippets, the running time are also different between:

    for (i = 0; i < 100000000; i++)
        memmove(dest, src, n);

and

    for (i = 0; i < 100000000; i++)
        memmove2(dest, src, n);


The results depend on the relationship between dest and src, where n denotes the length of string to be copied:

                dest < src | dest==src | dest > src | dest > src + n
    memmove         4.020       0.321       18.070      4.036
   memmove2         3.148       0.892       3.096       3.295

The running time is in second. It is obvious that while the memmove2 function outperforms the standard function in cases where dest!=src, the memmove is better when dest==src.


(Compiled using gcc 3.4.4)

No comments:

Post a Comment

(Coding && Eating) || Sleeping