Re: Returning a string array from a function



Adam wrote:
Hi,

I'd like to return an (arbitrary length) string array from a function so
that after calling the array I've got a list of strings I can access.

First problem: functions cannot return arrays. A fairly
close alternative is to use a function that returns a pointer
to something, with the tacit understanding that the pointed-to
something is the first one in an array of somethings.

Why is the distinction important? Because the pointer just
points to one single something, and conveys no information about
how many additional somethings might follow it. What *that*
means is that you need some other way to communicate the count.
Two popular approaches are to give the function an extra argument
that points to a place where it can store the count, or to store
a special value (NULL, perhaps) in an array slot after all the
"payload" positions, in the same way the end of a string is
marked by an extra '\0' character.

After much perusing of the internet I found a related answer here (by
Eric Sosman) which involved creating an array of pointers and using
that, so it looks something like:


void foo(char *sptr[], int items)
{
char buff[50];
while (--items >= 0)
{
sprintf (buff, "foo%d", items);
sptr[items] = malloc(strlen(buff) + 1);
if (sptr[items] == NULL)
exit(0);
strcpy (sptr[items], buff);
}
}
int main(void)
{
int total = 10;
char *p[total];

Are you using a C99 compiler? Prior to C99, array
dimensions had to be compile-time constants, but `total'
is a variable.

foo(p, total);
printf(p[1]);
return 0;
}


This works a treat (I can access my strings, e.g. the printf line) but
how would I do a similar thing if I don't know how long my list is going
to be? E.g. "total" only gets found out inside foo.

One way is to proceed more or less as above, but to
have foo() tell its caller how many of the sptr[] slots
actually got filled in; this number might be the value of
the function foo(), or you could use a NULL "sentinel" as
mentioned above. If you do this, the meaning of the second
argument should change from "make N strings" to "sptr[] has
at least N slots, but perhaps no more." This allows foo()
to avoid storing past the end of the sptr[] array if `total'
turns out to be larger than the caller anticipated.

Another way is for foo() to allocate the sptr "array" by
itself, using malloc(). If foo() later discovers that the
allocation was too small, it can use realloc() to make it
larger. When foo() is finished, it returns a pointer to the
start of the dynamically-allocated sptr "array" (and uses
communicates the count by some other means).

A variation on the second theme may be simpler if foo()
can discover `total' in one pass, then malloc() an sptr
"array" of what is now known to be the correct size, then
make a second pass to fill in the elements.

The latter two methods share one important feature (or
drawback): Since foo() allocates memory dynamically and leaves
it allocated when it returns, the responsibility for free()ing
the memory falls on someone else. This needs to be prominently
mentioned in the documentation for foo(), lest unwary callers
leak scads of memory by calling foo(), using its results for a
while, and then just "dropping them on the floor."

--
Eric Sosman
esosman@xxxxxxxxxxxxxxxxxxxx
.



Relevant Pages