The followup function to memcpy is memmove which performs the same operations but with the guarantee that overlapping memory regions will not cause any issue. The stipulation added by memmove is that "copying takes place as if the n characters from the object pointed to by s2 are first copied into a temporary array of n characters that does not overlap the objects pointed to by s1 and s2, and then the n characters from the temporary array are copied into the object pointed to by s1." The prototype is as follows

void *
memmove(void *s1, const void *s2, size_t n);

As with memcpy, the return value is not very useful and provides no way of indicating an error. Similarly, s2 is a const void * meaning that we cannot modify the data to which it points.

The "Obvious" Approach

Given the wording of extra stipulation, the logical approach would be to allocate a chunk of new temporary memory, copy from s2 to the temporary region, copy from the temporary region to s1, and finally free the temporary region. Something like this:

char *pDst          = s1;
const char *pSrc    = s2;
char *pTmp          = NULL;

/* Error checking */

pTmp = malloc(n);

if (NULL == pTmp)
{
    return s1;
}

memcpy(pTmp, pSrc, n);
memcpy(pDst, pTmp, n);

free(pTmp);
pTmp = NULL;

return s1;

However, this has an issue if the call to malloc fails. From implementing memcpy we already know that we can copy objects backwards to account for object overlap, so if the malloc fails we can just do that.

char *pDst          = s1;
const char *pSrc    = s2;
char *pTmp          = NULL;

/* Error checking */

pTmp = malloc(n);

if (NULL != pTmp)
{
    memcpy(pTmp, pSrc, n);
    memcpy(pDst, pTmp, n);

    free(pTmp);
    pTmp = NULL;
}
else
{
    /* Copy forward or backward to handle object overlap */
    if (pDst < pSrc)
    {
        while (0 < n--)
        {
            *(pDst++) = *(pSrc++);
        }
    }
    else
    {
        pDst += n;
        pSrc += n;

        while (0 < n--)
        {
            *(--pDst) = *(--pSrc);
        }
    }
}

return s1;

However, this is extra logic and code that's unnecessary. We can use our same strategy from memcpy from the beginning and not worry about malloc failing, using extra local variables, or having a new branch in the code to check malloc's return value.

The Simple Approach

By removing the use of malloc and memcpy, our memmove implementation looks like so:

char *pDst          = s1;
const char *pSrc    = s2;

/* Error checking */

/* Copy forward or backward to handle object overlap */
if (pDst < pSrc)
{
    while (0 < n--)
    {
        *(pDst++) = *(pSrc++);
    }
}
else
{
    pDst += n;
    pSrc += n;

    while (0 < n--)
    {
        *(--pDst) = *(--pSrc);
    }
}

return s1;

This should look familiar because it's exactly the same code found in our implementation of memcpy. In fact, we can even reuse the same error checking and once added, memmove will be implemented identically to memcpy. So, why don't we just call one function straight from the other? Well, we could call memmove from memcpy because the former operates under stricter guidelines that also fulfill the job of the latter. However, we could not call memcpy from memmove because the former makes no guarantee about copying between overlapping objects and this is a requirement for memmove.

Testing

Since memmove provides similar functionality to memcpy we can also reuse our tests by replacing every call to memcpy with a call to memmove. See the memcpy post for the tests.

Conclusion

In the end, this is a copy of our implementation of memcpy and we could slightly clean up the code by calling memmove from memcpy. We can reuse the same error checking to end at the following implementation for memmove.

void *
memmove(void *s1, const void *s2, size_t n)
{
    char *pDst          = s1;
    const char *pSrc    = s2;

    if ( !s1 ||
         !s2 ||
        /* Check for wrapping while copying */
        ((((unsigned long) -1) - ((unsigned long) s1)) < n) ||
        ((((unsigned long) -1) - ((unsigned long) s2)) < n))
    {
        return s1;
    }

    /* Copy forward or backward to handle object overlap */
    if (pDst < pSrc)
    {
        while (0 < n--)
        {
            *(pDst++) = *(pSrc++);
        }
    }
    else
    {
        pDst += n;
        pSrc += n;

        while (0 < n--)
        {
            *(--pDst) = *(--pSrc);
        }
    }

    return s1;
}
comments powered by Disqus