19

From the Linux manpage for memmove(3)

The memmove() function copies n bytes from memory area src to memory area dest. The memory areas may overlap: copying takes place as though the bytes in src are first copied into a temporary array that does not overlap src or dest, and the bytes are then copied from the temporary array to dest.

Instead of allocating a temporary array and copy the values twice we could just do the following:

void *my_memmove(void *dest, const void *src, size_t n) {
  signed char operation;
  size_t end;
  size_t current;

  if(dest != src) {
    if(dest < src) {
      operation = 1;
      current = 0;
      end = n;
    } else {
      operation = -1;
      current = n - 1;
      end = -1;
    }

    for( ; current != end; current += operation) {
      *(((unsigned char*)dest) + current) = *(((unsigned char*)src) + current);
    }
  }
  return dest;
}

In this implementation we simply take care of the position where we begin to copy.

Is there a drawback in my implementation?

Note: I won't actually use my implementation. I'm just curious.

MarcDefiant
  • 6,649
  • 6
  • 29
  • 49
  • 5
    `dest < src` is undefined behavior when `dest` and `src` point to different blocks. Function `memmove()` is specified to work when `dest` and `src` point to the same block, but it is not specified that it has to be called with pointers to the same block. See the related question http://stackoverflow.com/questions/4023320/how-to-implement-memmove-in-standard-c-without-an-intermediate-copy – Pascal Cuoq Nov 12 '12 at 07:13
  • 18
    I think you missed "as though" in manpage. It does not actually work that way. – dbrank0 Nov 12 '12 at 07:17
  • You can compare your implementation with [FreeBSD's `bcopy`](http://fxr.watson.org/fxr/source/string/bcopy.c?v=FREEBSD-LIBC), the underlying code for their memmove(). It also supports copying backwards, without a temporary buffer. – Volker Stolz Nov 12 '12 at 07:18
  • Your implementation is actually incorrect/invalid. In the expression `current += operation`, operation is converted to `size_t` and thus becomes `SIZE_MAX`, not -1. – R.. GitHub STOP HELPING ICE Nov 12 '12 at 07:28
  • @R.. Why does this matter? `current + (size_t)-1` still computes `current - 1`. – Pascal Cuoq Nov 12 '12 at 07:30
  • Indeed, you're right. I was thinking the `(size_t)-1` was being added directly to a pointer, but it's not; it's modifying `current` in a well-defined way. – R.. GitHub STOP HELPING ICE Nov 12 '12 at 07:34

1 Answers1

41

You can look at some source code for memmove here, here, here, and here.

What you'll notice is that they don't actually make a temporary array. The man pages are written to help you understand what it is doing logically, not actually. Hence, they say "as though".

What memmove() actually does is copy the bytes from src to dest, and it copies forward if dest < src (which is essentially the same thing as memcpy), and backwards otherwise.

The difference between memcpy and memmove is that memcpy blindly copies forward - which is why dest and src should not overlap. But memmove takes the precaution of ensuring the overlap will not screw up the end result.

nwn
  • 583
  • 7
  • 23
cegfault
  • 6,442
  • 3
  • 27
  • 49
  • 2
    Excellent points. The C source to Linux memmove is [here](http://sourceware.org/git/?p=glibc.git;a=blob;f=string/memmove.c;h=bf7dcc162770503ed62a262f627bfa526fc5a0d7;hb=HEAD), but it's largely irrelevant because the actual `memmove` is implemented [in hand-optimized assembly](http://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/x86_64/memcpy.S;h=9e693f2a9806fa8f6ce7e26f37b1808e952396d3;hb=HEAD), which definietely doesn't copy to a temp array. As a curiosity, it is defined exactly the same as `memcpy`, because on its target architecture there is no penalty from doing it that way. – user4815162342 Nov 12 '12 at 07:49