33

The function memcpy is defined as:

void* memcpy(void* s1, void* s2, size_t n)

and the 1990 ISO standard (ISO 9899:1990) defines the function as:

Description

The memcpy function copies n characters from the object pointed to by s2 into the object pointed to by s1. If copying takes place between objects that overlap, the behaviour is undefined.

Returns

The memcpy function returns the value of s1

But...why? s1 itself doesn't get modified. It seems that newer C standard inherited this. So I'm wondering why the original authors of the standard made this function return anything at all, and why it returns what it does.

15
  • 5
    This is just a hunch, but it could be because the string manipulation functions already did this (strcpy) etc. That just changes the question to “… the rationale for having strcpy return one of its inputs?” Commented Jun 21, 2022 at 14:16
  • 1
    I suppose so, yes. I'm reading "The Standard C Library" by Plauger (written in 1990 or so), and he does go into some very interesting detail about some of the stdlib stuff and why the committee, of which he was a part, chose to do what they did. It is very amusing to read what he thinks about errno. But things like this are just taken for granted. Commented Jun 21, 2022 at 14:24
  • 2
    I really enjoyed that book. Intriguingly, the K&R C book (2nd edition) discusses void strcpy(char *s, char *t) (no return value) and then just says “The strcpy in the standard library (<string.h>) returns the target string as its function value.” Commented Jun 21, 2022 at 15:16
  • 8
    For the curious, Plauger's choice words for errno and errno.h can be found here: archive.org/details/cprogramming0000koch/page/47/mode/1up
    – Jim Nelson
    Commented Jun 21, 2022 at 18:02
  • 4
    A tip on understanding almost anything in the C standard: Many C implementations existed for years before the language was standardized. For the most part, the standard writers simply chose which aspects of existing implementations to codify, with a distinct desire to break as little existing code as possible. Many things were simply inherited from existing implementations and weren't purposefully or intentionally designed by the standards committee.
    – bta
    Commented Jun 22, 2022 at 19:32

6 Answers 6

34

In early versions of the C language, every function would return something, whether or not the caller would make use of the returned value. Generally, the return value of a function would be whatever happened to be in some particular register of the appropriate type. If code exited a function without making any effort to set the register to something meaningful, and calling code ignored the contents of the register in question, having the function nominally return a meaningless value was simpler and easier than providing a means of having functions not return a value.

I don't think any particular thought was put into the question of what functions like memcpy, strcpy, or strcat should return, but the authors of the Standard didn't want to simply leave the return value unspecified. Since there may have been platforms where functions that don't return a value would be processed differently from those that do, giving such functions a void return type could have broken code that calls the functions without including the appropriate standard header.

I don't think any particular effort was made to have the functions return the most useful value. More likely, the authors of the Standard wanted to have the functions return some specified value, and so they somewhat arbitrarily picked a value to be returned.

2
  • 38
    I don't see it as arbitrary. In my view, strcpy and memcpy are analogous to assignment operators. If a=56*3, as an expression, returns the value of a, it makes some philosophical sense for strcpy or strcat to return the new string, and for memcpy to return the newly filled buffer.
    – Nimloth
    Commented Jun 21, 2022 at 19:49
  • 4
    @Nimloth: It is a consistent but bad design choice for strcat and strcpy, though, discarding the string-length / end-pointer information they had to compute during the operation. This is why stpcpy exists (in POSIX and various other implementations, but not ISO C): My answer on strcpy() return value took some guesses at possible asm / compiler / code-style reasons for the design decision. Commented Jun 23, 2022 at 3:09
21

So that you can write

s1 = memcpy (s2, memcpy (s3, memcpy (s4, s5)));

which is probably not particularily useful (for string concatenation using strcat, it is, however, and the memxxx and strxxx functions use aligned function signatures).

25
  • 3
    For string concatenation, using strcat(strcat(strcat(dest, string1), string2), string3) is gratuitously inefficient. I see no reason language or libraries should make gratuitously inefficient constructs more convenient than equally simple but more efficient ones.
    – supercat
    Commented Jun 21, 2022 at 20:20
  • 11
    A function which behaved like strcat, except returning the address of the terminating zero byte that was written to the destination, would support the same chaining syntax efficiently.
    – supercat
    Commented Jun 21, 2022 at 20:36
  • 3
    One possibly-useful construct could be return memcpy(s1, s2) to avoid a register load (before compilers became clever enough). But AFAICT the return value of strcpy and strcat is never used in Unix V7, so I’m not convinced such constructs were a factor. Commented Jun 22, 2022 at 4:49
  • 5
    I often use p = memcpy(malloc(whatever), x, whatever) when I don't care about failling allocations. Commented Jun 22, 2022 at 8:30
  • 4
    @tofro: To the contrary, the Standard explicitly specifies that adding one to the address of the last element of an array will yield a pointer that cannot be directly dereferenced, but it it may be used in both relational and equality comparisons, may have a negative value added to it to yield a valid pointer to an array element, and may be used with the pointer-difference operator to measure the distance from other pointers into or just past the array.
    – supercat
    Commented Jun 22, 2022 at 17:16
14

In the original K&R version of C, functions "returning" void simply did not exist (more generally, void did not exist).

So they initially had to return something (even if it was never used), and once that was defined, you simply couldn't remove the return value as someone, somewhere may have used it. It was changed from char * to void * sometime in history, but that did not break compatibility.

So, if you had to return something, some possible choices could have been:

  • returning a flag indicating whether the operation was successful or not. But other than triggering a memory fault and hence halting execution, memcpy can hardly fail
  • returning the number of bytes copied
  • returning the address of the buffer, by analogy with other functions that do the same
  • returning the address of the end of the buffer

In all those cases, the return value would be either one of the inputs or something easily derived from them. The choice is probably arbitrary, and then going along the same lines as other functions is probably the most natural choice.

7

The basis for ISO C in 1990 was ANSI C in 1989 ('C89').

There is an actual companion document for C89 called Rationale for American National Standard for Information Systems -- Programming Language -- C in which the X3J11 committee explains why they did what they did.

With respect to memcpy, that document says (4.11.1)

memcpy, memset, memcmp, and memchr have been adopted from several existing implementations. The general goal was to provide equivalent capabilities (...)

That's about it for the official Rationale. Anything more would be speculation by us.

But that's as I understand the committee's role in general - to ratify existing consensus rather than invent anything new.

(Sometimes invention was required to handle incompatible approaches: 4.11.2 does add further words about the addition of memmove, to resolve the issues around speed (memcpy) versus generality (memmove)).

3

Like most standardization processes, the C standard documented commonalities of existing practice. So the core reason that the standard required memcpy to return its first argument is that pre-standardization C implementations did this, and pre-standardization C code made use of the fact that they did.

supercat's answer, which does not answer the question, has good speculation about the possible historical origin of the behavior, which might or might not be correct. If you want to research this in detail, there is source code for historical early versions of Unix available which might shed light on whether there was originally an intent to return the value or whether it just happened to be in the right register.

3
  • 1
    memcpy just follows strcpy. I believe I read somewhere Ritchie discussing this as being historically related to B's representation of an array as having a separate pointer word, so you couldn't return a much-more-useful pointer to the array element after the copied data (to the 0 terminator in the case of strings). So that's why Unix did what it did, and (as you say) that became established practice and later standardized.
    – dave
    Commented Jun 23, 2022 at 17:23
  • @another-dave - that business about B's representation leading to the situation in C - I would never have guessed that, thank you. Especially because I've always wondered why it didn't return a pointer-to-after-the-copied-data (strcpy especially) so that you could (more) efficiently run one after the other ...
    – davidbak
    Commented Jun 23, 2022 at 17:55
  • I wish I could recall where I read it :-(
    – dave
    Commented Jun 23, 2022 at 18:07
-4

c was a stack-oriented language. In a simple implementation, return value is reserved on the stack, the parameters are pushed on to the stack, and local variables are reserved on the stack, and when the function returns --- the parameters and local variables are still there.

By default, any implementation that takes the '1st parameter' position as the 'return value' position returns the first parameter as the return value.

So memcpy and similar functions returned the first parameter just by default, because it was a simple implementation.

5
  • 3
    Even though arguments and local variables are often (but definitely not always) on the stack, I expect many (most?) implementation to use a register for the return value. See en.wikipedia.org/wiki/Calling_convention for a few examples. For those using registers both for arguments and return values (and the same ones) your argument may stand.
    – jcaron
    Commented Jun 23, 2022 at 8:45
  • @jcaron, that's why I wrote "was", and used the past tense in the final sentence.
    – david
    Commented Jun 23, 2022 at 9:27
  • 2
    @david But it wasn't so even on early implementations return value came in a register.
    – Raffzahn
    Commented Jun 23, 2022 at 12:34
  • Really? Where would the return value of a function like int GetSomeInt(); be stored? I'm pretty sure that the earliest C compiler I worked on (Aztec C on Z-80) returned values in the HL register way back in the early 80s.
    – Flydog57
    Commented Jun 23, 2022 at 20:45
  • 1
    Putting the return value on the stack, rather than in some conventional register, would be highly unusual on the PDP-11 (which is what is meant by 'early C implementations', not microcomputer systems). To do that on the -11 requires at least popping the return link into a scratch register, pushing the function value, and then doing an indirect jump to the popped link; contrast this with writing the return value to a register and doing a simple subroutine return.
    – dave
    Commented Jun 23, 2022 at 23:51

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .