40

The function memmove is defined like this:

void *memmove(void *dest, const void *src, size_t n);

In the Linux manual page, it says:

RETURN VALUE
The memmove() function returns a pointer to dest.

Why isn't the function just defined as void memmove(…) when it always returns one of the input parameters? Can the return value be different from dest?

Or is the return value really always dest and it is just done to be able to compose the function in some creative ways?

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
Martin Ueding
  • 8,245
  • 6
  • 46
  • 92

3 Answers3

45

memmove will never return anything other than dest.

Returning dest, as opposed to making memmove void, is useful when the first argument is a computed expression, because it lets you avoid computing the same value upfront, and storing it in a variable. This lets you do in a single line

void *dest = memmove(&buf[offset] + copiedSoFar, src + offset, sizeof(buf)-offset-copiedSoFar);

what you would otherwise need to do on two lines:

void *dest = &buf[offset] + copiedSoFar;
memmove(dest, src + offset, sizeof(buf)-offset-copiedSoFar);
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • @usr but it is the reason why most of string and memory function return the final pointer, or that you can do `a = b = c = d`. Returning a value is very cheap, and BTW `void` is a _new_ invention. Original K&R C didn't have the `void`. – Giacomo Catenazzi Oct 12 '16 at 19:54
  • 6
    Make it return nothing. This is not the concern of this function. String functions return a pointer because the pointer is calculated in an O(N) way (if I'm wrong about that string functions should not return a pointer either). – usr Oct 12 '16 at 20:16
  • @usr: It would make sense for functions like strcat to return a pointer to the end of the destination, but that isn't what they do. Instead, they return a pointer to the start of a string. – supercat Oct 12 '16 at 22:21
  • 7
    This is a pretty weak argument, IMO. If you showed my the first example in a code review, I'd ask you to turn it into the second. Why is it `memmove`'s job to help you make one-liners? A much better return value would have been `dest + n`, because then you could chain things together: `dest = memcpy(dest, ...); dest = memcpy(dest.. ); etc`. – GManNickG Oct 13 '16 at 00:45
  • 2
    @GManNickG To me, big part of the beauty of C is in its amazing one-liners. My personal favorite is `while ((*dest++ = *src++));` for string copying. Once I saw the semicolon at the end, I was hooked! I do agree with you that returning the end of the buffer, rather than its beginning, would make a lot more sense. – Sergey Kalinichenko Oct 13 '16 at 01:46
  • 10
    @dasblinkenlight I'd call it obscure rather than amazing, at least for anyone other than C programmers. If I want to copy strings in one line, I'd rather just use `strcpy` anyway. – Malcolm Oct 13 '16 at 09:27
  • 5
    The one liners in C are fascinating and kind of nice but they should be considered art, not production worthy code. – usr Oct 13 '16 at 14:53
  • 2
    @usr I think the general philosophy is that returning something is better than returning nothing. Even if the returned value is redundant, there might be situations where it's useful, like the chained calls in other answers. Why preclude such things? – Barmar Oct 18 '16 at 18:54
  • @Barmar because it makes the API harder to understand at extremely low value added. It adds a completely unrelated computation to the API. That makes it harder to learn. If the return value is actually used the code is now harder to understand and verify as well. – usr Oct 18 '16 at 19:00
  • @usr C programmers are expected to be reasonably intelligent (they're supposed to be able to avoid array overflows without any automated checking). The designer presumably felt that this feature had more value than you do. You're entitled to your opinion, but that doesn't make it wrong. – Barmar Oct 18 '16 at 19:06
  • @Barmar who cares what they are expected to do. This is reducing productivity at no other benefit and is therefore objectively wrong. I understand the the designer disagrees with me. Why else would he have done that?! Many C APIs are disgustingly bad designed. Back then programmers were less experienced in API design. – usr Oct 18 '16 at 19:47
  • @usr You said this is a confusing API. If this is too confusing for a programmer, they shouldn't be using C, because it has far more confusing things than this, such as all the details you need to worry about to avoid buffer overflows. – Barmar Oct 18 '16 at 19:51
  • @Barmar I agree with GManNickG and usr. This is a really weak argument. This "feature" serves no purpose. When C sacrifices readability and makes it more difficult for novices to understand, it generally has a better reason than "it makes cool one-liners". It's not the job of the API to do this, and C programmers shouldn't be burdened with arbitrary complexity just because we're "expected" to. I would be more interested to know if there's a historical reason for this API. – PC Luddite Oct 25 '16 at 20:50
  • @Malcolm yes, but `memmove` can overlap :O – GuillemVS Dec 30 '20 at 19:27
26

As per C11, chapter §7.24.2.1 and §7.24.2.2

void *memcpy(void * restrict s1, const void * restrict s2, size_t n);

[...] The memcpy function returns the value of s1.

and,

void *memmove(void *s1, const void *s2, size_t n);

[...] The memmove function returns the value of s1.

So, the functions will always return the pointer to the destination buffer, that's by design.

Now coming to the why part, many functions are designed this way to make chaining of function calls possible. That way, you can have a call to memmove() as an argument to another function, where the copied value (i.e, the pointer to dest) is going to be of some use.

For example, you can write the shorter one

 puts(memmove(dest_buffer, src_buffer, welcome_message_size));

instead of the longer one

 memmove(dest_buffer, src_buffer, welcome_message_size);
 puts(dest_buffer);
Community
  • 1
  • 1
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
13

The idiom of returning the exact value of one of the arguments (of pointer type) exists in order to support "chained" function calls (also see strcpy, strcat etc). It allows you to write some repetitive code as a single expression statement instead of splitting it into multiple statements. For example

char buffer[1024];
printf("%s\n", strcat(strcat(strcpy(buffer, "Hello"), " "), "World"));

struct UserData data_copy;
some_function(memcpy(&data_copy, &original_data, sizeof original_data));

Even if you don't like this style of organizing code and prefer doing the same through multiple statements, the overhead of returning an [unnecessary] pointer value is virtually non-existent.

One can even say that the value of this idiom increased a bit after the introduction of compound literals in C99. With compound lterals this very idiom allows one to write the same code without introducing a named intermediate variable

printf("%s\n", strcat(strcat(strcpy((char [1024]) { 0 }, "Hello"), " "), "World!"));

some_function(memcpy(&(struct UserData) { 0 }, &original_data, sizeof original_data));

which makes sense since in most cases that named variable is supposed to be short-lived, is not needed afterwards, and only clutters the namespace.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 1
    I don't do much with C, but wouldn't `sizeof data_copy` in your compound literals example have to be `sizeof original_data`, since `data_copy` no longer exists? – Pokechu22 Oct 12 '16 at 17:45
  • 1
    Of course, the example with strcat will be brilliantly inefficient since each strcat operation will start by scanning the destination to find the null byte, then concatenate the new data, and then return the *start* of the destination so the next call will have to scan through both the old and new data to find the new location of the zero byte. – supercat Oct 12 '16 at 22:22
  • 1
    @supercat: Yes, it is probably safe to say that `strcat` is a useless function. It is used in my answer for purely illustrative purposes. – AnT stands with Russia Oct 12 '16 at 22:45