memmove() Usage

I

Ian

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


The function will delete a portion of a string at the give positions.

For example:

char tmpstr[10] = "Hello";
strdel(tmpstr,0,0);

This would change "Hello" to "ello".

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...

Thanks
 
R

Robin Haigh

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


The function will delete a portion of a string at the give positions.

For example:

char tmpstr[10] = "Hello";
strdel(tmpstr,0,0);

This would change "Hello" to "ello".

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...

It is if called incorrectly (though it's not obligatory to protect all
functions against being called wrongly).

Looks like s[offset] is the first byte to delete, and s[count] is the last
byte to delete?

Then if count == offset you delete one byte, and if count == offset - 1 you
delete 0 bytes.

But if count < offset - 1 you end up moving bytes to the right, so you can
run out of space.
 
I

Ian

But if count < offset - 1 you end up moving bytes to the right, so
you can run out of space.

But isn't memmove() supposed to protect that from happening? I thought
the whole purpose of memmove() was to provide a safe way to copy
memory...

The man page for memmove() says the following:

"The memmove() function copies len bytes from string src to string dst.
The two strings may overlap; the copy is always done in a non-
destructive manner."


I take that to mean no matter what offset and count is given, memmove()
is responsible for making sure that nothing goes wrong; no matter what.
 
P

pete

Ian said:
I take that to mean no matter what offset and count is given,
memmove()
is responsible for making sure that nothing goes wrong;
no matter what.

All library functions do what they're supposed to do,
when you call them with correct arguments.

If you have a string in an arrray:
char array[] = "abc";
and you want to copy the third character onto the first,
so that it becomes "cbc", then you can use memcpy:

memcpy(array, array + 2, 1);


If you have a string in an arrray:
char array[] = "abc";
and you want to copy the last two characters to the
front so that it becomes "bcc", then use memmove:

memmove(array, array + 1, 2);

.... because memcpy is undefined
when the source and destination overlap.
 
R

Robin Haigh

Ian said:
But isn't memmove() supposed to protect that from happening? I thought
the whole purpose of memmove() was to provide a safe way to copy
memory...

The man page for memmove() says the following:

"The memmove() function copies len bytes from string src to string dst.
The two strings may overlap; the copy is always done in a non-
destructive manner."


I take that to mean no matter what offset and count is given, memmove()
is responsible for making sure that nothing goes wrong; no matter what.


It only means that it won't scribble over the data it's about to copy. It
doesn't know where the end of the destination buffer is, so it can't protect
itself against running off the end.

Incidentally, this manpage is using the word string more loosely than the
standard. Strings as defined have null-byte terminators, but with
memmove(), null-terminators aren't required or respected.
 
R

Ryan Reich

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.
strdel(tmpstr,0,0);

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.
 
I

Ian

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


I tested the function, but it doesn't seem to work correctly.

Let's use this string as an example:

char string[] = "racecar";


Using your function like this:

strdel(string,0,3);

should've turned "racecar" into "car", but it didn't.

My functions seems to work correctly for this test, but I'm not sure
why. memmove() is really confusing me...
 
R

Ryan Reich

This NULL shouldn't be there, since the function returns type void.
The compiler might complain, though it's probably harmless if it
actually compiles.
memmove(s + offset, s + offset + count,
strlen(s) - count - offset);
}


I tested the function, but it doesn't seem to work correctly.

Let's use this string as an example:

char string[] = "racecar";


Using your function like this:

strdel(string,0,3);

should've turned "racecar" into "car", but it didn't.

My functions seems to work correctly for this test, but I'm not sure
why. memmove() is really confusing me...

Perchance, is it instead giving you "ecarcar"? Because that's what it
gives me when I test your example. This is exactly what I expected it
to produce, though not what you did. It also perfectly illustrates the
subtle error I mentioned before. Think about it (hint: think about how
C represents strings). Also, if you wanted "car", you should have
called the function as

strdel(string, 0, 4);

to delete the first four characters, "race".
 
I

Ian

Perchance, is it instead giving you "ecarcar"? Because that's
what it gives me when I test your example. This is exactly what I
expected it to produce, though not what you did. It also
perfectly illustrates the subtle error I mentioned before. Think
about it (hint: think about how C represents strings). Also, if
you wanted "car", you should have called the function as

strdel(string, 0, 4);

to delete the first four characters, "race".

Using your example: strdel(string,0,4) gives me "carecar". I tested
this both on Digital Mars and Borland compilers in ANSI C mode. They
both do the same.
 
R

Ryan Reich

Ian said:
Using your example: strdel(string,0,4) gives me "carecar". I tested
this both on Digital Mars and Borland compilers in ANSI C mode. They
both do the same.

Well, I don't expect it to depend on the compiler. You still haven't
found the error in my function, though. Ask yourself: what sort of
error could lead to the string having extra characters at the end?
 
D

Default User

maybe is:
memmove(s + offset, s + offset + count + 1, strlen(s) - offset - count
-1);

It's difficult to tell what you are talking about (see below).



Brian
 
P

Pedro Graca

Ian said:
is this little function 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);
}

I know I'm not answering your question; just wanted to point out that
"strdel" is reserved for the implementation as is any identifier that
starts with "str" and is followed by a lowercase letter (and maybe a few
more).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top