In an interview, I was asked to write an implementation of strcpy
and then fix it so that it properly handles overlapping strings. My implementation is below and it is very naive. How do I fix it so that:
char* my_strcpy(char *a, char *b) {
if (a == NULL || b == NULL) {
return NULL;
}
if (a > b) {
//we have an overlap?
return NULL;
}
char *n = a;
while (*b != '\0') {
*a = *b;
a++;
b++;
}
*a = '\0';
return n;
}
int main(int argc, char *argv[])
{
char str1[] = "wazzupdude";
char *after_cpy = my_strcpy(str1 + 2, str1);
return 0;
}
EDIT:
So one possible implementation based on @Secure's answer is:
char* my_strcpy(char *a, char *b) {
if (a == NULL || b == NULL) {
return NULL;
}
memmove(a, b, strlen(b) + 1);
return a;
}
If we don't rely on memmove
, then
char* my_strcpy(char *a, char *b) {
if (a == NULL || b == NULL) {
return NULL;
}
if (a == b) {
return a;
}
// case1: b is placed further in the memory
if ( a <= b && a + strlen(a) > b ) {
char *n = a;
while(*b != '\0') {
*a = *b;
a++; b++;
}
*a = '\0';
return n;
}
// case 2: a is further in memory
else if ( b <= a && b + strlen(b) > a ) {
char *src = b + strlen(b) - 1; // src points to end of b
char *dest = a;
while(src != b) {
*dest = *src;
dest--; src--; // not sure about this..
}
*a = '\0';
return a;
}
}
There is no portable way to detect this. You have to do pointer comparisons, and these are only defined within the same object. I.e. if the two strings do not overlap and are in fact different objects, then the pointer comparisons give you undefined behaviour.
I would let the standard library handle this, by using memmove(a, b, strlen(b) + 1)
.
EDIT:
As Steve Jessop pointed out in the comments, there actually is a portable but slow way to detect overlap in this case. Compare each address within b with the first and last address of a for equality. The equality comparison with ==
is always well defined.
So you have something like this:
l = strlen(b);
isoverlap = 0;
for (i = 0; i <= l; i++)
{
if ((b + i == a) || (b + i == a + l))
{
isoverlap = 1;
break;
}
}
EDIT 2: Visualization of case 2
You have something like the following array and pointers:
S t r i n g 0 _ _ _ _ _ _ _
^ ^
| |
b a
Note that b + strlen(b)
results in a pointer to the terminating \0. Start one behind, else you need extra handling of edge cases. It is valid to set the pointers there, you just can't dereference them.
src = b + strlen(b) + 1;
dst = a + strlen(b) + 1;
S t r i n g 0 _ _ _ _ _ _ _
^ ^ ^ ^
| | | |
b a src dst
Now the copy loop which copies the \0, too.
while (src > b)
{
src--; dst--;
*dst = *src;
}
The first step gives this:
src--; dst--;
S t r i n g 0 _ _ _ _ _ _ _
^ ^ ^ ^
| | | |
b a src dst
*dst = *src;
S t r i n g 0 _ _ _ 0 _ _ _
^ ^ ^ ^
| | | |
b a src dst
And so on, until src
ends up equal to b
:
S t r i S t r i n g 0 _ _ _
^ ^
| |
b a
src dst
If you want it a bit more hackish, you could compress it further, but I don't recommend this:
while (src > b)
*(--dst) = *(--src);