memcpy vs memmove

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
PlayOS

memcpy vs memmove

Post by PlayOS »

Hi,

Just wondering if someone can tell me what the difference is between these two functions. I know that memmove will accept overlapping src and dst parameters, but how is the copy performed?

Thanks.
Schol-R-LEA

Re:memcpy vs memmove

Post by Schol-R-LEA »

I cannot give a definitive answer, but from what I see of the function descriptions in the various references, the only difference is in how they behave regarding overlapping memory areas; memmove() guarantees that the data will be moved to the to argument, while memcpy()'s behavior in such instances is undefined.

If I were to speculate, I would assume that the reason that there are two separate functions is due to a combination of efficiency and history. It is clear that regardless of implementation, memcpy() can be written to be more efficient than memmove(); the two obvious ways to implement them is as either an assembly-language block move, if the current system supports one, or as a simple for loop moving n bytes from array to array one at a time. In the fmorer case, to ensure that it copies correctly, you would have to use a second buffer and copy the data twice (see my solution to the scrolling issue for an example). The x86 version of the block move could also solve the problem by switching the direction flag (see the second solution below), but that's probably more trouble than it's worth.

In the latter case, you can take the same approach, as shown here (code sampled from True64 Unix C Compiler Reference, modified for ANSI compatibility):

Code: Select all

   void *memmove(void *to, const void *from, size_t count) {
           char * t1 = to;
           const char * t2 = from;
           char * t3 = malloc(count);
           size_t i;
           for(i=0; i<count; i++) t3[i] = t2[i];
           for(i=0; i<count; i++) t1[i] = t3[i];
           free(t3);
           return from;
   }
If you wanted to avoid that, you'd need to make sure that any overlapping data is moved first, to avoid overwriting it before it can be moved. Basically, this means that if the to and from overlap in a way that from has a higher address than to, you would copy starting at the low addresses of from; while if to is higher in memory than from, you would need to copy from the high address in from, moving downwards in memory. Here's a fairly efficient example of such an implementation (based on a version found in a page entitled "Unix Incompatibiliy Notes", modified for ANSI compatibility):

Code: Select all

    void *memmove(void *to, void *from, size_t count)
    {
        void *retval = from;
        if (from > to)
             for ( ; count > 0; count--)
                 *(to++)= *(from++);
        else
             for (to+= count-1, from+= count-1; count > 0; count--)
                 *(to--)= *(from--);
        return retval;
    }
From what I've read, also, memcpy() predates memmove(), which seems to have been added later when the issue with memcpy()'s handling of overlaps was identified as a problem. I'd assume that memmove() arose out of a specific need, most likely buffer management and scrolling in full-screen text editors, where overlapping areas are the norm rather than the exception. Whatever the origin, it remained rarely used because memcpy() was the established version, and was more efficient in the majority of cases. Mind you, this is speculation on my part; I know, for example, that there is at least one other similar function, bcopy(), which was apparently the standard version on older BSD systems, replacing both memcpy() and memmove(). I don't know what the actual history of them is, off hand.

BTW, given the existence of both malloc() and memcpy() functions, there is one other trivial implementation of memmove():

Code: Select all

   void *memmove(void *to, const void *from, size_t count) {
           void *buff = malloc(count);
 
           memcpy(buf, from, count); 
           memcpy(to, buf, count);
           free(buf);
           return from;
   }
HTH. C&CW.
Post Reply