Skip to Content
Course content

238: Whiteboard Practice: Implementing memcpy and strcpy From Scratch

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Look, if you've ever sat through a technical interview for a systems role, you've probably seen these two functions pop up on a whiteboard. They seem trivial—you're just moving data from point A to point B—but they're actually the perfect way to prove you understand how pointers, memory addresses, and the null terminator actually work under the hood.

Think of memory like a long row of mailboxes. Imagine I give you two different sets of boxes. In the first scenario, I tell you: "Take exactly 10 items from the first row and put them in the second row." You don't care what's inside the boxes—letters, packages, or junk mail—you just count to ten and move them. That's memcpy. In the second scenario, I tell you: "Start at the first box and keep moving items to the second row until you find an empty box." You don't know how many items there are; you're just hunting for that specific "empty" signal. That's strcpy.

Handling the Blind Byte Transfer

When we implement memcpy, we have to deal with void *. Since we don't know if we're copying integers, doubles, or a custom struct, we have to treat the memory as a sequence of raw bytes. In C, the smallest addressable unit is a char, so we cast our pointers to unsigned char * to ensure we move exactly one byte at a time.

void *my_memcpy(void *dest, const void *src, size_t n) {
    unsigned char *d = (unsigned char *)dest;
    const unsigned char *s = (const unsigned char *)src;

    for (size_t i = 0; i < n; i++) {
        d[i] = s[i];
    }

    return dest;
}

I've returned dest here because that's what the standard library does, allowing you to chain function calls. A quick warning: this simple version doesn't handle overlapping memory regions (where src and dest overlap). For that, you'd need memmove, which checks the addresses and copies backward if necessary. But for a standard memcpy implementation, this byte-by-byte approach is the gold standard.

Hunting for the Null Terminator

strcpy is a different beast because it's "length-agnostic." It relies entirely on the sentinel value '\0' to know when to stop. The biggest mistake I see people make here is forgetting to copy that null terminator itself into the destination buffer. If you stop the loop before copying the '\0', your destination string isn't actually a string—it's just a sequence of characters that will cause a segmentation fault the moment you try to printf it.

char *my_strcpy(char *dest, const char *src) {
    char *save = dest; // Keep a pointer to the start to return it later

    while (*src != '\0') {
        *dest = *src;
        dest++;
        src++;
    }

    *dest = '\0'; // The crucial final step
    return save;
}

You'll often see this written in a very terse, "clever" way in old C textbooks: while ((*dest++ = *src++));. While that's impressive in a code golf competition, I'd advise against it in a real codebase. It's harder to debug and less readable. Stick to the explicit loop; your teammates (and your future self) will thank you.

Why the 'const' Matters

You noticed I used const void *src and const char *src. This isn't just for show. It's a contract. By marking the source as const, you're telling the compiler—and the person using your function—that memcpy and strcpy will never accidentally modify the original data. In systems programming, making your intentions explicit with const is one of the best ways to prevent "mystery bugs" where data changes unexpectedly.




📋 Practical Task

Exercise: Implementing a Bounds-Checked String Copier

Standard strcpy is notoriously dangerous because it doesn't know how big the destination buffer is, leading to buffer overflows. Your task is to implement a "safe" version called my_strncpy_safe.

Write a function with the following signature: char *my_strncpy_safe(char *dest, const char *src, size_t dest_size)

Your implementation must:

  • Copy characters from src to dest.
  • Stop copying if the end of the source string ('\0') is reached.
  • Stop copying if you have reached dest_size - 1 characters, ensuring there is always room for the null terminator.
  • Always null-terminate the dest string, regardless of whether the source was truncated or fit perfectly.
  • Return the pointer to the dest string.

Test your function by attempting to copy a 20-character string into a buffer of only 10 bytes, and verify that the resulting string is exactly 9 characters long followed by a '\0'.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.