Please look into this inexplicable behavior and output of memcpy() for overlapping memory blocks
Asked Answered
C

1

-1

After reading the following about memcpy(), I proceeded to read about memmove():

To avoid overflows, the size of the arrays pointed by both the destination and source parameters, shall be at least num bytes, and should not overlap (for overlapping memory blocks, memmove is a safer approach).(LINK)

And after checking the program used to illustrate the working of memmove() I decided to tweak it by using memcpy() instead to see how different is the output.To my surprise,they are the same even if it's a case of overlapping memory blocks.Here is the program and the output,and I have proceeded to describe my confusion after that:

#include <stdio.h>
#include <string.h>

int main ()
{
  char str[] = "memmove can be very useful......";
  //memmove (str+20,str+15,11);
  memcpy(str+20,str+15,11);  //Simply used memcpy instead of memmove
  puts (str);
  return 0;
}

Output memmove can be very very useful.

This output is the same as it is for memmove().And here are my confusions:

1) Why is output same for both?Since there is no intermediate buffer used in case of memcpy(),I expect the copy to start by copying the character in str+15 position to the str+20 position (overwriting what is there),character in str+16 position to str+21 position, and so on till character in str+20 position,which has by now changed to the character in str+15 position, to be copied to the str+25 position.But it's not so,there is no overwriting and it's acting as if an intermediate buffer is used to write the exact original string.Here's an illustration:

memmove can be very useful......  //Original positions before memcopy
               ^    ^
            str+15  str+20

memmove can be very vseful......
                    ^ copies str+15 to str+20

memmove can be very veeful......
                     ^ copies str+16 to str+21
memmove can be very verful......
                      ^ copies str+17 to str+22
memmove can be very veryul......
                       ^copies str+18 to str+23
memmove can be very very l......
                        ^ copies str+19 to str+24
memmove can be very very v......
                         ^ I expect 'v' to be copied from str+20 to str+25
                           as str+20  now has 'v',not 'u'
memmove can be very very ve.....
                          ^ I expect 'e' to be copied from str+21 to str+26 
                            as str+21 now has 'e' not 's'

Then why is memcpy() copying it as memmove can be very very useful, instead of memmove can be very very very v ?

2) Now a minor secondary question arising from it.The following is said about memmove() (LINK)

Copying takes place as if an intermediate buffer were used, allowing the destination and source to overlap.

What exactly is as if here?Isn't an intermediate buffer really used for memmove()?

Cromagnon answered 14/5, 2013 at 5:30 Comment(11)
@Koushik Not undefined.Unless an intermediate buffer is used,I really expect the character at str+20 to be overwritten.Raddled
"Unless an intermediate buffer" it will be implementation defined. really cant tell.Mow
@Koushik Since the second parameter of memcpy is a constant,why are we even allowed to write onto that without generating errors for memcpy()?Raddled
compiler has no way of knowing which memory you are accessing. it only has the pointer to the source. you are saying that the source wont be modified by decalring the second parameter as const but you break the promise soo after which compiler cant help.so doing anything to a const declared location is UB(even if the const location is not read only)Mow
you can do this const int i = 10; ......int *ptr = &i;..*ptr = 100;..printf("%d",i). which is UB. but i broke the covenant. compiler warns but i can do it. you cant even be sure wherther it will printMow
Have you considered the output when you run this program on codepad?Sawyere
@undefinedbehaviour What about it?What did you find?plz tell.Raddled
@Rüppell'sVulture Click the link... and I've warned against using cplusplus as a C reference. Google "opengroup memcpy" and "opengroup memmove".Sawyere
@undefinedbehaviour Jeez,never knew Cplusplusreference would betray me one day....Whom to trust?Raddled
@undefinedbehaviour LOL..Your codepad link gave such a deep insight.It's so even in Ideone ideone.com/kd1GpH .Sigh,even codeblocks betrayed me.It gave that confusing output,not the expected output.How to mend CodeBlocks to behave this way as codepad or ideone?Raddled
@Rüppell'sVulture Don't. Clearly the behaviour you expected isn't portable. Seek something more portable so you have infinite guarantees that things will continue to work well into the future.Sawyere
H
6

If the objects overlap, the behaviour of memcpy is undefined. There's little point in trying to reason about undefined behaviour. Since it is undefined it defies reason. You know the rules, and they are clearly documented. If the objects overlap, use memmove.

As for the use of "as if", that is to specify behaviour but not place any limitations on implementation. This allows the library implementor to use whatever method they see fit, so long as the end result is the same as using an intermediate buffer. For example, the implementation could detect that the objects do not overlap and so avoid using an intermediate buffer for performance reasons.

Holifield answered 14/5, 2013 at 5:37 Comment(17)
The prototype of memcpy() is void * memcpy ( void * destination, const void * source, size_t num ).ie, the source is supposed to be a constant.Why then memcpy() even let the source string be altered/written onto?Raddled
@Rüppell'sVulture, The documentation clearly says that it does not check for overlaps so you should not pass overlapping buffers so the const makes sense.Matilda
@Matilda Why aren't we warned or get an error that we are trying to change the value of a constant?Raddled
It's UB for source and dest to overlap. And the onus on you is to obey the contract and not pass overlapping objects. One of the goals of the C standard library is to provide high performance code. And this is achieved in this case by making the programmer guarantee pre-conditions rather than the library function spending time checking pre-conditions.Holifield
@Rüppell'sVulture, because you are passing in pointers, it cannot warn you without checking it online which is the reason why it is undefined.Matilda
Because that's not what const means. what const means is that memcpy() will have to treat the second pointer, source, as nonmodifiable, it doesn't mean that the memory pointed to is nonmodifiable. If you lie to memcpy() and tell it that the target address is safely disjoint from the source, it believes you. "Undefined behaviour" means that if the regions do overlap, any sort of screwup that may (but is not required) to happen is your own fault, and not a bug in the library.Ermey
@ElchononEdelson Doesn't const void * source mean the memory pointed to is nonmodifiable? And isn't the declaration for a non-modifiable pointer void *const source?Raddled
@Rüppell'sVulture, the interface of memcpy is void *memcpy(void * restrict s1, const void * restrict s2, size_t n); (with restrict) so it is clearly stating that the two pointers that are passed to it shouldn't alias. If you pass them in a way that this isn't guaranteed that's your fault. And const qualification doesn't mean that the target isn't modifiable. It just means that memcpy isn't allowed to modify through that pointer. That is a contract, nothing else.Lohr
@Matilda Do you mean we can get away using const void* source even if source is not const-qualified in its declaration?Raddled
No, const void *source means the memory pointed to must be treated as nonmodifiable through this pointer.Ermey
@Rüppell'sVulture, const in the function signature means that the function has no intentions of modifying that parameter. it does not mean that the object passed is immutable.Matilda
@ElchononEdelson through this pointer only or through any pointer?Raddled
@ElchononEdelson Shall I conclude that it means memory pointed to must be treated as nonmodifiable through this pointer. but other pointers can modify it?Raddled
@Rüppell'sVulture inside the function the memory that is promised to be unchanged should be unchanged even if you access it through another pointer(which you should really declare as a pointer to const memory).Mow
const is not relevant to this question.Fourway
@R.. Why so?What if we don't want the function to which the pointer argument is passed to change the original value at the pointed address?Please elaborate a little.Raddled
Passing a pointer-to-const has nothing to do with whether the callee can modify the pointed to data in a well-defined way. Casting back to a pointer to the non-const-qualified version of the type is perfectly well-defined, and modifying the object through the resulting pointer is also perfectly well-defined as long as the original object pointed to was not const-qualified. In other words, in int x=0; foo((const int *)&x); the function can legally modify x; only in the case of const int x=0; foo(&x) does modification result in undefined behavior.Fourway

© 2022 - 2024 — McMap. All rights reserved.