memmove
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;
}