2

I looked at http://www.opensource.apple.com/source/xnu/xnu-2050.24.15/libsyscall/wrappers/memcpy.c

and didn't understand the following :

1-

inside

void * memcpy(void *dst0, const void *src0, size_t length) {
    char *dst = dst0;
    const char *src = src0;

line:

if ((unsigned long)dst < (unsigned long)src) {

How can we cast dst to an unsigned long ? it's a pointer !

2- Why do they sometimes prefer forward copying and sometimes backwards??

Klas Lindbäck
  • 33,105
  • 5
  • 57
  • 82
user1047069
  • 935
  • 2
  • 13
  • 25
  • 1
    Probably because the creator of the function knows that on the XNU platform, the type `unsigned long` is the same size as a pointer? There probably are some static assertions elsewhere that makes sure this is the case. – Some programmer dude Nov 15 '13 at 14:25
  • To have the quality of this question enhanced it would be nice to have the declarations of `dst` and `src` in place. This way the question would be clear without having to follow the link provided (which however could change over time). – alk Nov 15 '13 at 14:30
  • @alk Good idea. I added it to the question. – Klas Lindbäck Nov 15 '13 at 14:59
  • pointer is an address, address can be represented as unsigned long – zoska Nov 15 '13 at 15:01
  • The forward/backward issue is because of possible overlap of the memory locations. – Charlie Burns Nov 15 '13 at 15:01
  • @zoska This isn't back by the C Standard, I suppose. – alk Nov 15 '13 at 15:03
  • I wonder why the casts are in place at all? This should work perfectly fine the same way without casting the pointers to `unsigned long`. – alk Nov 15 '13 at 15:05
  • IIUC this specific memcpy() is part of the syscall implementation, not of the standard library. There might be specific reasons to limit the pointers to a certain range of values, or perform other trickery on them (maybe the syscall has to translate them to a special range / address window) But it could just as well be caused by sloppy programming, of course. – joop Nov 15 '13 at 15:26
  • 1
    @alk - you will get the compiler complaining if you compare "incompatible pointers". The cleanest way to code this, in my mind, would be `if((void*)dst < (void*)src)`. No need to know how big the pointers are, no compiler complaints (any pointer can be cast to `void *`). – Floris Nov 15 '13 at 15:37

2 Answers2

4

You are right, this implementation is non-portable, because it is assuming that a pointer is going to fit in unsigned long. This is not guaranteed by the standard.

A proper type for this implementation would have been uintptr_t, which is guaranteed to fit a pointer.

Community
  • 1
  • 1
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
1

When comparing a void* and char* pointer, the compiler will give a warning (gcc -Wall):

warning: comparison of distinct pointer types lacks a cast

I imagine that the developer decided to "make the warning go away" - but the correct way to do this is with a void* cast (which is portable):

if((void*) dst < (void*) src) {

As for the second point - as was pointed out, you have to take care of overlapping memory locations. Imagine the following 8 characters in successive memory locations:

 abcdefgh

Now we want to copy this "3 to the right". Starting with a, we would get (with left-to-right copy):

abcdefgh
abcaefgh
   ^
abcabfgh
   ^^
abcabcgh
   ^^^

etc until you end up with

abcabcabcab
   ^^^^^^^^

When we wanted to get

abcabcdefgh

Starting from the other end, we don't overwrite things we still have to copy. When the destination is to the left of the source, you have to do it in the opposite direction. And when there is no overlap between source and destination, it doesn't matter what you do.

Floris
  • 45,857
  • 6
  • 70
  • 122