[This test has been edited lightly since its original posting.]

Newsgroups: comp.lang.c
From: scs@eskimo.com (Steve Summit)
Subject: Re: Newbie: Should a function malloc or used fixed strings?
Message-ID: <CrHu2G.D3B@eskimo.com> [revised]
References: <2tmtoi$1q4@hk.super.net>
Date: Thu, 16 Jun 1994 14:11:01 GMT

In article <2tmtoi$1q4@hk.super.net>, Rodney Haywood writes:
> I am writting a function that converts a number to a worded string,
> normally used in a cheque print program. The length of the string
> returned is not know until it is parsed by the function and could be
> quite long 123 chars. I am not sure if it is best to pass a predefined
> string to the function and have it place the result in it or to have
> the function return a pointer to a sting that it allocated with malloc().
> I can see pros and cons for the two. Using malloc gives more work to the
> caller as they will need to free() the memory every time they are finished
> with the current value of the string, otherwise if they called it 1000
> time it would continue to use memory that was no longer required. The
> other way round the user could create a buffer to receive the string and
> do the memory allocation themselves if they needed to keep it around.
> If every thing is fixed lenght strings then there could be lots of wasted
> space.

This is an excellent question, and you've already identified two of the major tradeoffs. Others have already mentioned others; I'll mention a few more that are important to me.

What I worry about most when designing a routine which must return a string is the caller's convenience, especially if the routine will be used a lot. The questions to ask are:

  1. How much of a nuisance would it be for the caller to pass in a buffer?

  2. How much of a nuisance would it be for the caller to free() the returned pointers?

  3. In what sorts of contexts will the call likely appear?

  4. How likely is it that the caller will need to manipulate several returned strings simultaneously?

Since the problem statement mentions a check printing program, we have an example which nicely illustrates these tradeoffs. If the routine accepts a number like 1234.56 and returns a string like ``One Thousand, Two Hundred and Thirty Four Dollars and Fifty Six cents'', it is likely that the call to it will appear in a context like

	extern char dollarformat();
	printf("Pay to: %-40s  $%.2f\n", payee, amount);
	printf("%-50s\n", dollarformat(amount));

The above fragment sets the stage, and ignores dollarformat()'s return buffer allocation. (It's also dollarocentric; apologies.) Let's look at how the call would appear using various allocation strategies:

If the caller must pass in the buffer, we have (call this ``method A''):

	char amountbuf[51];
	printf("%-50s\n", dollarformat(amount, amountbuf, 51));

If the routine returns a malloc'ed pointer, we have (``method B''):

	char *amountret;
	amountret = dollarformat(amount);
	printf("%-50s\n", amountret);

or, if we don't mind some condensation,

	printf("%-50s\n", amountret = dollarformat(amount));

Finally, if the routine returns the infamous ``pointer to a static buffer which is overwritten with each call,'' we can simply use (``method C''):

	printf("%-50s\n", dollarformat(amount));
(For this third technique, if you're not familiar with it, dollarformat's implementation looks something like

	char *
	dollarformat(double amount)
	static char retbuf[RETBUFSIZE];

	/* ... format amount into retbuf ... */

	return retbuf;

, where the static declaration of retbuf is vital, though somewhat frequently overlooked.)

Functions which return pointers to static buffers (i.e. which use method C) have the annoying property that you can't call them twice and keep using both return values, and this can be the source of stubborn bugs. For example, given the third implementation, you can't do

	char *p1 = dollarformat(amount1);
	char *p2 = dollarformat(amount2);
	printf("%s %s\n", p1, p2);
	printf("%s %s\n", dollarformat(amount1), dollarformat(amount2));
However, if it is unlikely that the caller will want to manipulate several values simultaneously, and if the caller won't mind making explicit copies in those few instances when it does, static return values (method C) can be very convenient, because the caller never (well, hardly ever) has to worry about buffer allocation. That's why the technique is popular in the traditional C and Unix run-time libraries (e.g. ctime(), getpwuid()), although I'm sure this is one of the things that drives people who are less than fond of C and Unix up a tree.

Note that although static return buffers (method C) are usually used when the size of (or an upper bound on) the return buffer is known, the function can also keep a single static pointer to an area of dynamically-allocated memory which it grows (with realloc) as big as it needs to be for any single call. (It might use a second static variable to keep track of the buffer's current size.)

If the caller wishes to manipulate several return values simultaneously and the function requires the caller to pass in a buffer (method A), the caller merely has to remember to pass distinct buffers:

	char amountbuf1[51], amountbuf2[51];
	printf("%s %s", dollarformat(amount1, amountbuf1, 51),
			dollarformat(amount2, amountbuf2, 51));
	(void)dollarformat(amount1, amountbuf1, 51);
	(void)dollarformat(amount2, amountbuf2, 51);
	printf("%s %s", amountbuf1, amountbuf2);

Note that when the caller passes in a buffer, it can either be statically or dynamically allocated; the function doesn't care.

Note also that routines which accept caller-supplied buffers must always allow the size of that buffer to be specified, so that the routine can guarantee not to overflow it. Remember gets() vs. fgets()!

Finally, note that routines which return strings, in buffers provided by their callers, are often described with words like ``the routine returns its first argument'' (or, in this example, its second argument), which sounds silly at first glance (if the caller knows it, why return it?), but which makes handing the string to some other routine (in these examples, printf) very easy.

Finally, when the function returns a pointer to dynamically allocated memory (method B), it can be maximally easy for the caller to manipulate multiple values simultaneously. However, it can be a real nuisance to keep a handle on the return value, in order to free it. Obvious invocations like

	printf("%s\n", dollarprint(amount));
are blatant (though not necessarily obvious) memory leaks.

Other people have mentioned efficiency, both of buffer space and allocation overhead. In situations where strings are of arbitrary length, fixed-length arrays are decidedly inferior: they're either too small and they overflow, or they're much too large and they waste space.

If an upper bound on the string's size can be determined and it's a few hundred characters or less, though, using fixed-sized arrays won't waste much (unless hundreds of arrays are allocated simultaneously), and their convenience is a distinct advantage.

To avoid the perils of fixed-size arrays, people who've been around for a while usually recommend dynamic allocation, although it must be admitted that the explicit management of allocation which this forces upon you, and the problems of memory leaks and dangling pointers (which were recently pointed out to be opposites -- nice observation, whoever it was!), can be quite daunting; they probably represent the single biggest hurdle when learning C, and they account for a constant background level of bugs, some of them so subtle they're never found, even in the most sophisticated programs written by the most sophisticated programmers.

In most cases, though, malloc/free overhead is not a problem. (There are certainly exceptions, but that fact hardly implies that every program must eschew malloc, or write its own, or use fancy storage management wrappers.)

In the end, if the preceding discussion hasn't made it clear, there is no one best method of returning a string (or other aggregate) from a function in C, nor is there a single ironclad reason always to prefer fixed-size or dynamically-allocated arrays. Usually you can find a good solution for any particular case that comes up, but it's a good idea to think about it a bit to make sure you make a good choice.

The three return value methods above (A, B, and C) do not exhaust the possibilities. Some routines (getcwd, many GNU routines) use a combination of methods A and B, returning a pointer to malloc'ed memory if the caller passes a null pointer instead of a buffer. People unclear on the concept occasionally try to return strings from functions to their callers via temporary files, but we don't need to say much more about that approach. Finally, there's a cunning extension on method C, which has evidently been independently discovered by a number of people, which returns pointers to static buffers and allows the caller to use up to N (but not N+1) return values simultaneously, but that's an article for another day.

Steve Summit