Ian said:
I read the FAQ about the differences between memcpy() and memmove().
Apparently memmove() is supposed to be safer. Just to make sure I
understand the concept of memmove(), can someone tell me if this
little function is using memmove() correctly?
void strdel(char *s, size_t offset, size_t count) {
if ((!s) || (offset > strlen(s)) || (count > strlen(s))) return;
memmove(s+offset,s+(count+1),strlen(s)-count);
}
May I make some suggestions about the code? First, you don't need all
those checks at the top: you can combine the last two into
offset + len > strlen(s)
which tests what you really care about: that the end of the requested
region to delete does not lie past the end of the string itself
(actually, my opinion is that you could easily "round down" if this
happens, though this has its own usage problems, which you should think
about). This makes it (marginally) faster and also easier to
understand.
Second, I think you're calling memmove() incorrectly. It's declared as
void *memmove(void *dest, void *src, size_t n);
so your first argument should be the desired destination (s + offset;
that's good) and your second should be the start of the string you're
moving (s + offset + count: you have to skip past the entire deleted
substring to the beginning of the remainder, and move that remainder
back). Finally, your third argument should be the number of characters
you move, which is not strlen(s) - count but strlen(s) - count -
offset, since you move nothing before the end of the deleted portion.
With that in mind, you should write the function as
void strdel(char *s, size_t offset, size_t count)
{
if (!s || offset + count > strlen(s))
return NULL;
memmove(s + offset, s + offset + count,
strlen(s) - count - offset);
}
Another, very subtle, error remains, which you should find before you
actually use this thing. Note that the semantics are now a little
different than yours: offset is the index of the first character to
delete (or the number of characters before it; i.e. its offset) and
count is the number of characters to delete. I didn't make that choice
consciously, however; it just came from the C string-indexing
conventions.
The function will delete a portion of a string at the give positions.
For example:
char tmpstr[10] = "Hello";
For what it's worth, there's no need to include the [10] in this
definition; the compiler will automatically construct an array of the
correct size for the string. This prevents you from accidentally
writing something less than the length of the string, too.
With my version of the function, this would be
strdel(tmpstr, 0, 1);
This would change "Hello" to "ello".
From looking at the function as you wrote it, it would seem that the
only reason this does what you think it should is that you happen to be
deleting only one character at the beginning of the string. If you
were doing more than one character, or if it were not at the beginning,
things would go wrong. If you are going to test your functions, try to
tailor the tests to exploit the algorithm as much as they can.
It appears to work correctly, but I just want to make sure I am using
memmove() correct in this case, and that the function is not open to
an overflow of some kind...
As others have pointed out, memmove() does not protect against
overflow; it merely ensures that if you are reading from and writing to
the same memory locations, data is not destroyed before the location it
occupies is read from. The sort of thing that might happen with a
quick-and-dirty implementation of memcpy() is that you call it such:
char str[] = "Hello";
memcpy(str + 1, str, 4);
and end up with "HHHHH" in str, because the function would read the
first character and write it in the second, then read the second (which
is now 'H' rather than 'e') and write it in the third, etc. memmove()
is more careful than this.