12

Although the subject is discussed many times, I haven't found any satisfying answer so far. When to return data from a function by return or to pass a reference to change the data on address? The classic answer is to pass a variable as reference to a function when it becomes large (to avoid stack copying). This looks true for anything like a structure or array. However returning a pointer from a function is not uncommon. In fact some functions from the C library to the exact thing. For example:

char *strcat(char *dst, const char *src);

Always returns a pointer to destination even in case of an error. In this case we can just use the passed variable and leave the return for what it is (as most do).

When looking at structures I see the same thing happening. I often return pointers when functions only need to be used in variable initialization.

char *p = func(int i, const char *s);

Then there is the argument that stack coping variables is expensive, and so to use pointers instead. But as mentioned here some compilers are able to decide this themselves (assuming this goes for C as well). Is there a general rule, or at least some unwritten convention when to use one or the other? I value performance above design.

Community
  • 1
  • 1
Yorick de Wid
  • 859
  • 11
  • 19
  • 3
    Sounds more like a http://programmers.stackexchange.com/ question – cup Mar 11 '15 at 13:38
  • Returning pointers usually results in the lifetime manage problem the object pointed to, because 90% of the time, the pointee is newly constructed by the function. – user3528438 Mar 11 '15 at 13:42
  • The example with `strcat` is true for old style API. The new replacement return error code: `errno_t strcat_s( char *strDestination, size_t numberOfElements, const char *strSource )`. – i486 Mar 11 '15 at 13:46
  • @i486 - is strcat_s() (et. al _s functions) universally supported? – ryyker Mar 11 '15 at 14:08
  • @ryyker AFAIK thats the Windows API. glibc doesn't have safe functions (yet) – Yorick de Wid Mar 11 '15 at 14:09
  • @YorickdeWid - Yes, I was being subtle in my comment. I meant to point out that that comment's (from i486) phrasing seemed to bias a C implementation that is not universally accepted. – ryyker Mar 11 '15 at 14:25

2 Answers2

14

Start by deciding which approach makes the most sense at the logical level, irrespective of what you think the performance implications might be. If returning a struct by value most clearly conveys the intent of the code, then do that.

This isn't the 1980s anymore. Compilers have gotten a lot smarter since then and do a really good job of optimizing code, especially code that's written in a clear, straightforward manner. Similarly, parameter passing and value return conventions have become fairly sophisticated as well. The simplistic stack-based model doesn't really reflect the reality of modern hardware.

If the resulting application doesn't meet your performance criteria, then run it through a profiler to find the bottlenecks. If it turns out that returning that struct by value is causing a problem, then you can experiment with passing by reference to the function.

Unless you're working in a highly constrained, embedded environment, you really don't have to count every byte and CPU cycle. You don't want to be needlessly wasteful, but by that same token you don't want to obsess over how things work at the low level unless a) you have really strict performance requirements and b) you are intimately familiar with the details of your particular platform (meaning that you not only know your platform's function calling conventions inside and out, you know how your compiler uses those conventions as well). Otherwise, you're just guessing. Let the compiler do the hard work for you. That's what it's there for.

John Bode
  • 119,563
  • 19
  • 122
  • 198
-2

Rules of thumb:

  1. If sizeof(return type) is bigger than sizeof(int), you should probably pass it by pointer to avoid the copy overhead. This is a performance issue. There's some penalty for dereferencing the pointer, so there are some exceptions to this rule.
  2. If the return type is complex (containing pointer members), pass it by pointer. Copying the local return value to the stack will not copy dynamic memory, for example.
  3. If you want the function to allocate the memory, it should return a pointer to the newly allocated memory. It's called the factory design pattern.
  4. If you have more than one thing you want to return from a function - return one by value, and pass the rest by pointers.
  5. If you have a complex/big data type which is both input and output, pass it by pointer.
liorda
  • 1,552
  • 2
  • 15
  • 38
  • 1
    Where are they come from? I mean your `Rules of thumb`. Could you present the reference links? – chain ro Mar 11 '15 at 13:52
  • My experience, some common sense and good industry practices. – liorda Mar 11 '15 at 13:52
  • No 5 applies to any data type...and this is the least of the incomplete answers you give here.... – Pandrei Mar 11 '15 at 13:55
  • 1
    I would have gone with sizeof(return) < sizeof(int*). Also, if the copy constructor/assignment operator/etc of the return type do non standard things even if the class size is small, its worth doing by reference. I've seen copy ctor's that initiated web connections. People put some crazy stuff in strange places. – LawfulEvil Mar 11 '15 at 13:55
  • @Pandrei Regarding 5 - if it's a POD, passing it by pointer won't make a lot of sense. You should always get the result as the return type, unless one of the rules apply. – liorda Mar 11 '15 at 14:00
  • If it's a big POD, passing it in by value can easily blow up the stack. That's what @Pandrei was saying in his #2. – user3528438 Mar 11 '15 at 14:04
  • in the context of C (which we are here) what should be a POD? – Jens Gustedt Mar 11 '15 at 14:05
  • @JensGustedt I think POD in this context are everything besides pointer types. – liorda Mar 11 '15 at 14:18
  • @liorda, I know what a POD is, only in the context of C this distinction makes no sense at all. This is terminology for C++ and not C. – Jens Gustedt Mar 11 '15 at 14:23
  • @JensGustedt I borrowed the term from C++ to mean what I wrote - anything but pointer types. My apologies if it caused confusion. – liorda Mar 11 '15 at 14:37