pointers-memory · intermediate · ~20 min

Safe overlapping copy (memcpy vs memmove)

Why memmove exists, and how to implement it without UB.

Challenge

Implement void *my_memmove(void *dst, const void *src, size_t n) that copies n bytes from src to dst correctly even when the regions overlap.

The contract matches the standard memmove:

  • Returns dst.
  • Behaviour is defined even if dst and src overlap (this is what distinguishes it from memcpy).

The trick:

  • If dst < src, copy forwards (low addresses first).
  • If dst > src, copy backwards (high addresses first).
  • If dst == src or n == 0, do nothing.

Examples

char buf[10] = "abcdefghij";
my_memmove(buf + 2, buf, 5);     // shift forward by 2
// buf -> "ababcdehij" — the overlap is safely handled

char buf[10] = "abcdefghij";
my_memmove(buf, buf + 2, 5);     // shift backward by 2
// buf -> "cdefgfghij"

Edge cases

  • dst == src: no-op.
  • n == 0: no-op.
  • Non-overlapping: forward or backward both work; pick forward for cache friendliness.

Why this matters

Most C developers learn that memcpy is faster than memmove and use it everywhere — until the day they shift bytes within the same buffer and watch their data corrupt. Implementing memmove's contract by hand cements the difference.

Input format

dst, src, byte count.

Output format

Returns dst.

Constraints

Pure C. No memcpy / memmove. Handle overlap correctly.

Starter code

#include <stddef.h>
void *my_memmove(void *dst, const void *src, size_t n) { /* TODO */ return dst; }

Common mistakes

Always copying forwards (corrupts when dst > src and they overlap). Comparing dst < src with signed types (use the unsigned pointers as uintptr_t or just unsigned char *).

Edge cases to handle

Aliasing; zero-length copy; one-byte copy.

Complexity

O(n).

Background lessons

Up next

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.