Arrays are ``second-class citizens'' in C. Related to the fact that arrays can't be assigned is the fact that they can't be returned by functions, either; that is, there is no such type as ``function returning array of ...''. In this chapter we'll study three workarounds, three ways to implement a function which attempts to return a string (that is, an array of char) or an array of some other type.
In the last chapter, we looked at some code for converting an integer into a string of digits representing its value. This operation is the inverse of the function performed by the standard function atoi. Suppose we wanted to wrap our digit-generating code up in a function and call it itoa. How would it return the generated string of digits? We'll use this example to demonstrate all three techniques. For simplicity, though, we won't repeat the do/while loop in each example function; instead, we'll simply call sprintf. (In fact, since calling sprintf is so easy, most C programs call it directly when they need to convert integers to strings, and consequently there is no standard itoa function.)
First, let's look at the way that won't work, so that we can set it aside and make sure we never use it. What if we wrote itoa like this?
char *itoa(int n) { char retbuf[25]; sprintf(retbuf, "%d", n); return retbuf; }This looks superficially reasonable, and it might well be what we'd write at first if we weren't being careful. (It might even seem to work, at first.) However, it has a serious, fatal flaw: let's think about that local array, retbuf. Since it's a regular local variable, it has automatic duration, which means that it springs into existence when the function is called and disappears when the function returns. Therefore, the pointer that this version of itoa returns is to an array which no longer exists by the time the caller receives the pointer. (Remember that the statement return retbuf; returns a pointer to the first character in retbuf; by the ``equivalence of arrays and pointers,'' the mention of the array retbuf in this context is equivalent to &retbuf[0].) When the caller tries to use the pointer, the string created by itoa might still be there, or the memory might have been re-used by some other function. Therefore, this first version of itoa is not adequate and not acceptable. Functions must never return pointers to local, automatic-duration arrays.
Since the problem with returning a pointer to a local array is that the array has automatic duration by default, the simplest fix to the above non-functional version of itoa, and the first of our three working methods of returning arrays from functions, is to declare the array static, instead:
char *itoa(int n) { static char retbuf[25]; sprintf(retbuf, "%d", n); return retbuf; }Now, the retbuf array does not disappear when itoa returns, so the pointer is still valid by the time the caller uses it.
Returning a pointer to a static array is a practical and popular solution to the problem of ``returning'' an array, but it has one drawback. Each time you call the function, it re-uses the same array and returns the same pointer. Therefore, when you call the function a second time, whatever information it ``returned'' to you last time will be overwritten. (More precisely, the information, that the function returned a pointer to, will be overwritten.) For example, suppose we had occasion to save the pointer returned by itoa for a little while, with the intention of using it later, after calling itoa again in the meantime:
int i = 23; char *p1, *p2; p1 = itoa(i); i = i + 10; p2 = itoa(i); printf("old i = %s, new i = %s\n", p1, p2);But this won't work as we expect--the second call to itoa will overwrite the string (stored in itoa's static retbuf array) which was stored by the first call. Instead of printing i's old and new value, the last line will print the new value, twice. Both p1 and p2 will point to the same place, to the retbuf array down inside itoa, because each call to itoa always returns the same pointer to that same array.
We can see the same problem in an even simpler example. Suppose we had never heard of the %d format specifier in printf. We might try to call something like this:
printf("i = %s, j = %s\n", itoa(i), itoa(j));where i and j are two different int variables. What will happen? Either the compiler will make the first call to itoa first, or the second. (It turns out that it's not specified which order the compiler will use; different compilers behave differently in this respect.) Whichever call to itoa happens second will be the one that gets to keep its return value in retbuf. The printf call will either print i's value twice, or j's value twice, but it won't be able to print two distinct values.
The moral is that although the static return array technique will work, the caller has to be a little bit careful, and must never expect the return pointer from one call to the function to be usable after a later call to the function. Sometimes this restriction is a real problem; other times it's perfectly acceptable. (Some of the functions in the standard C library use this technique; one example is ctime, which converts timestamp values to printable strings. When you see a cryptic sentence like ``The returned pointer is to static data which is overwritten with each call'' in the documentation for a library function, it means that the function is using this technique.) When this restriction would be too onerous on the caller, we should use one of the other two techniques, described next.
If the function can't use a local or local static array to hold the return value, the next option is to have the caller allocate an array, and use that. In this case, the function accepts at least one additional argument (in addition to any data to be operated on): a pointer to the location to write the result back to. Our familiar getline function has worked this way all along. If we rewrote itoa along these lines, it might look like this:
char *itoa(int n, char buf[]) { sprintf(buf, "%d", n); return buf; }Now the caller must pass an int value to be converted and an array to hold the converted result:
int i = 23; char buf[25]; char *str = itoa(i, buf);There are two differences between this version of itoa and our old getline function. (Well, three, really; of course the two functions do totally different things.) One difference is that getline accepted another extra argument which was the size of the array in the caller, so that getline could promise not to overflow that array. Our latest version of itoa does not accept such an argument, which is a deficiency. If the caller ever passes an array which is too small to hold all the digits of the converted integer, itoa (actually, sprintf) will sail off the end of the array and scribble on some other part of memory. (Needless to say, this can be a disaster.)
Another difference is that the return value of this latest version of itoa isn't terribly useful. The pointer which this version of itoa returns is always the same as the pointer you handed it. Even if this version of itoa didn't return anything as its formal return value, you could still get your hands on the string it created, since it would be sitting right there in your own array (the one that you passed to itoa). In the case of getline, we had a second thing to return as the formal return value, namely the length of the line we'd just read.
However, this second strategy is also popular and workable. Besides our own getline function, the standard library functions fgets and fread both use this technique.
When the limit of a single static return array within the function would be unacceptable, and when it would be a nuisance for the caller to have to declare or otherwise allocate return arrays, a third option is for the function to dynamically allocate some memory for the returned array by calling malloc. Here is our last version of itoa, demonstrating this technique:
char *itoa(int n) { char *retbuf = malloc(25); if(retbuf == NULL) return NULL; sprintf(retbuf, "%d", n); return retbuf; }Now the caller can go back to saying simple things like
char *p = itoa(i);and it no longer has to worry about the possibility that a later call to itoa will overwrite the results of the first. However, the caller now has two new things to worry about:
char *retbuf = malloc(25); if(retbuf == NULL) { fprintf(stderr, "out of memory\n"); exit(EXIT_FAILURE); }Now the function never returns a null pointer, so the caller doesn't have to check. (When malloc fails, the function doesn't return at all.)
In summary, we've seen three ways of ``returning'' arrays from functions, none of which is perfect. The static array technique is usually convenient for the caller, but only for functions which it's unlikely that the caller will be trying to call multiple times and retain multiple return values. (The static array technique is also definitely imperfect in that it violates the notion that calling code shouldn't need to know about the inner, implementation details of a called function.) The caller-passes-an-array technique is useful when the caller might have a number of calls to the function active, but when that number is small and fixed, so that the caller can easily declare and keep track of a number of return arrays (if necessary). Finally, when there might be an arbitrary number of calls to the function, or when maximum flexibility is otherwise needed, the function-calls-malloc technique is appropriate, but with its extra flexibility comes some costs, the most important of which is that the caller must remember to free the returned pointers.
Read sequentially: prev next up top
This page by Steve Summit // Copyright 1996-1999 // mail feedback