Question

I'm creating a function that returns a string. The size of the string is known at runtime, so I'm planning to use malloc(), but I don't want to give the user the responsibility for calling free() after using my function's return value.

How can this be achieved? How do other functions that return strings (char *) work (such as getcwd(), _getcwd(), GetLastError(), SDL_GetError())?

Was it helpful?

Solution

Your challenge is that something needs to release the resources (i.e. cause the free() to happen).

Normally, the caller frees the allocated memory either by calling free() directly (see how strdup users work for instance), or by calling a function you provide the wraps free. You might, for instance, require callers to call a foo_destroy function. As another poster points out you might choose to wrap that in an opaque struct, though that's not necessary as having your own allocation and destroy functions is useful even without that (e.g. for resource tracking).

However, another way would be to use some form of clean-up function. For instance, when the string is allocated, you could attach it to a list of resources allocated in a pool, then simply free the pool when done. This is how apache2 works with its apr_pool structure. In general, you don't free() anything specifically under that model. See here and (easier to read) here.

What you can't do in C (as there is no reference counting of malloc()d structures) is directly determine when the last 'reference' to an object goes out of scope and free it then. That's because you don't have references, you have pointers.

Lastly, you asked how existing functions return char * variables:

  • Some (like strdup, get_current_dir_name and getcwd under some circumstances) expect the caller to free.

  • Some (like strerror_r and getcwd in under other circumstances) expect the caller to pass in a buffer of sufficient size.

  • Some do both: from the getcwd man page:

As an extension to the POSIX.1-2001 standard, Linux (libc4, libc5, glibc) getcwd() allocates the buffer dynamically using malloc(3) if buf is NULL. In this case, the allocated buffer has the length size unless size is zero, when buf is allocated as big as necessary. The caller should free(3) the returned buffer.

  • Some use an internal static buffer and are thus not reentrant / threadsafe (yuck - do not do this). See strerror and why strerror_r was invented.

  • Some only return pointers to constants (so reentrancy is fine), and no free is required.

  • Some (like libxml) require you to use a separate free function (xmlFree() in this case)

  • Some (like apr_palloc) rely on the pool technique above.

OTHER TIPS

Many libraries force the user to deal with memory allocation. This is a good idea because every application has its own patterns of object lifetime and reuse. It's good for the library to make as few assumptions about its users as possible.

Say a user wants to call your library function like this:

for (a lot of iterations)
{ 
    params = get_totally_different_params();
    char *str = your_function(params);
    do_something(str);
    // now we're done with this str forever
}

If your libary mallocs the string every time, it is wasting a lot of effort calling malloc, and possibly showing poor cache behavior if malloc picks a different block each time.

Depending on the specifics of your library, you might do something like this:

int output_size(/*params*/);
void func(/*params*/, char *destination);

where destination is required to be at least output_size(params) size, or you could do something like the socket recv API:

int func(/*params*/, char *destination, int destination_size);

where the return value is:

< desination_size: this is the number of bytes we actually used
== destination_size: there may be more bytes waiting to output

These patterns both perform well when called repeatedly, because the caller can reuse the same block of memory over and over without any allocations at all.

There is no way to do this in C. You have to either pass a parameter with size information, so that malloc() and free() can be called in the called function, or the calling function has to call free after malloc().

Many object oriented languages (eg. C++) handle memory in such a way as to do what you want to, but not C.

Edit

By size information as an argument, I mean something to let the called function know the how many bytes of memory are owned by the pointer you are passing. This can be done by looking directly at the called string if it has already been assigned a value, such as:

char test1[]="this is a test";
char *test2="this is a test";  

when called like this:

readString(test1); // (or test2) 

char * readString(char *abc)
{
    int len = strlen(abc);
    return abc;
}

Both of those arguments will result in len = 14

However if you create a non populated variable, such as:

char *test3; 

And allocate the same amount of memory, but do not populate it, for example:

test3 = malloc(strlen("this is a test") +1);  

There is no way for the called function to know what memory has been allocated. The variable len will == 0 inside the 1st prototype of readString(). However, if you change the prototype readString() to:

readString(char *abc, int sizeString);  Then size information as an argument can be used to create memory:    

void readString(char *abc, size_t sizeString)
{
    char *in;
    in = malloc(sizeString +1);

    //do something with it  
    //then free it
    free(in);

}  

example call:

int main()
{
     int len;
     char *test3;

     len =  strlen("this is a test") +1; //allow for '\0'  
     readString(test3, len);
     // more code
     return 0;
}

You cannot do this in C.

Return a pointer and it is up to the person calling the function to call free

Alternatively use C++. shared_ptr etc

You can wrap it in a opaque struct.

Give the user access to pointers to your struct but not its internal. Create a function to release resources.

void release_resources(struct opaque *ptr);

Of course the user needs to call the function.

You could keep track of the allocated strings and free them in an atexit routine (http://www.tutorialspoint.com/c_standard_library/c_function_atexit.htm). In the following, I have used a global variable but it could be a simple array or list if you have one handy.

#include <stdlib.h>
#include <string.h>
#include <malloc.h>
char* freeme = NULL;
void AStringRelease(void)
{
    if (freeme != NULL)
        free(freeme);
}
char* AStringGet(void)
{
    freeme = malloc(20);
    strcpy(result, "A String");
    atexit(AStringRelease);
    return freeme;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top