DEV Community

Sijmen J. Mulder
Sijmen J. Mulder

Posted on

String copy in C

Originally posted at sjmulder.nl.

Casey of Molly Rocket posted four interview questions asked for his 1994 Microsoft intership.

This page is about the second: implementing a string copy function:

Implement strcpy(), which copies a string into another buffer (we don't care about the return value for now):

char *strcpy(char *src, char *dst);

Let's get this out of the way: using this function is usually a bad idea because you need to be absolutely sure that the destination buffer can fit the full source string. Instead, use strcpy_s() if available, strclpy(), or even snprintf() (snprintf(dst, sizeof(dst), "%s", src)).

Let's do a simple for-loop copy first:

void strcpy_1(char *src, char *dst)
{
        size_t len, i;

        len = strlen(src);

        for (i=0; i < len; i++)
                dst[i] = src[i];

        dst[len] = '\0';
}
Enter fullscreen mode Exit fullscreen mode

We find the length of the string, then copy it over one char at a time. Don't forget the null termination!

But since strlen has to walk the string to find the null terminator, we's looping over the string twice! See by expanding strlen:

void strcpy_2(char *src, char *dst)
{
        size_t len=0, i;

        for (i=0; src[i] != '\0'; i++)
                len++;
        for (i=0; i < len; i++)
                dst[i] = src[i];

        dst[len] = '\0';
}
Enter fullscreen mode Exit fullscreen mode

Let's put that in one loop:

void strcpy_3(char *src, char *dst)
{
        size_t i;

        for (i=0; src[i] != '\0'; i++)
                dst[i] = src[i];

        dst[i] = '\0';
}
Enter fullscreen mode Exit fullscreen mode

This works perfectly fine, but we can avoid having the i variable altogether by incrementing src and dst directly. Walking a pointer like that is a common idiom in C:

void strcpy_4(char *src, char *dst)
{
        while (*src != '\0') {
                *dst = *src;
                src++;
                dst++;
       }
}
Enter fullscreen mode Exit fullscreen mode

Our final change is to simplify this version by removing the explicit \0 comparison and folding the increment expressions into the assign statement - which may make your hair stand up but this access-and-increment pattern is so common that it's a useful trick to know:

void strcpy_5(char *src, char *dst)
{
        while (*src)
                *dst++ = *src++;

        *dst = '\0';
}
Enter fullscreen mode Exit fullscreen mode

Let's dissect that: *dst++ is *(dst++). The ++ here is post-increment, which means that first the old value is returned, and only after the statement the new value is assigned to dst. So first *dst = *src is performed, and only then are dst and src incremented - just like in the previous version.

Perhaps a lot to grasp for those unfamiliar with this pattern, but again it's a often-used solution to this common situation. For a different take, here's how OpenBSD implements the function with a for loop instead (and also returning the copied string, per spec):

char *
strcpy(char *to, const char *from)
{
    char *save = to;

    for (; (*to = *from) != '\0'; ++from, ++to);
    return(save);
}
Enter fullscreen mode Exit fullscreen mode

Appendum

Casey's video on this question is out now. His solution was:

for (int i=0; (dst[i] = src[i]); i++) ;
Enter fullscreen mode Exit fullscreen mode

I hadn't thought to use the result of the assignment as the loop condition. Now you also don't need the extra \0 assignment because that happens in the last iteration of the loop.

Another version from the video:

while (*dst++ = *src++) ;
Enter fullscreen mode Exit fullscreen mode

In a Borland C compiler of the time the first version generates faster code.

Comments welcome on Mastodon or below.

Top comments (0)