tags:

views:

225

answers:

6

When passing a char* as an argument to a function, should the called function do a free on that string? Otherwise the data would be "lost" right and the program would leak data. Or are char* handled in a special way by the compiler to avoid everyone from having to do free all the time and automatically deletes it one it goes out of scope? I pass "the string" to the function so not an instance to an already existing char*. Or should one use char[] instead? Just feels so dumb to set a fixed limit to the argument input.

+7  A: 

It sounds like you're asking about this usage:

void foo(char* str);
foo("test string");

This is a special case; "test string" is a constant string stored in the string table within the executable, and doesn't need to be freed. foo should actually take a const char* to illustrate that, and allowing string literals to be stored in non-constant char*s is deprecated in C++

Michael Mrozek
To expand on this a bit, the string literal "test string" exists as a 12-element array of char (const char in C++) with *static extent*, meaning the memory for it is allocated at program startup and held until the program exits. When you pass a string literal as a function argument, the compiler **does not** create a new instance of the string; instead, it passes a pointer to the existing instance.
John Bode
+6  A: 

Whether the function should do a free or not depends on who owns the string. This code is perfectly valid and doesn't result in any memory leak:

int main()
{
  char* s = malloc(.....);
  f(s);
  free(s);
}

The free can be performed inside function f as well if it takes the ownership of the string. However note that it is dangerous since you are assuming that string passed to function f is always allocated on heap using malloc or related functions. If a user passes pointer to a string allocated on stack your program will behave unpredicably.

On a general note, compiler doesn't do any special handling for memory management of strings. From compiler's point of view it is just a bunch of characters.

Naveen
Not just a stack-allocated string. If you pass `f()` a string literal, and `f()` tries to free it, you will also get into trouble.
ptomato
+14  A: 

Keep this simple principle in mind: "always free memory at the same level that you allocated it". In other words a function should never try to free memory that it itself has not allocated. A short example to clarify this:

#include "graphics.h"

// The graphics API will get a Canvas object for us. This may be newly allocated
// or one from a pool of pre-allocated objects. 
Canvas* canvas = graphics_get_canvas (); 

// If draw_image () frees canvas, that violates the above principle.
// The behavior of the program will be unspecified. So, just draw the image
// and return.
draw_image (canvas); 

// This is also a violation.
// free (canvas) ; 

// The right thing to do is to give back the Canvas object to the graphics API
// so that it is freed at the same 'level' where it was allocated. 
graphics_return_canvas (canvas);

Note that the function is not named graphics_free_canvas () or something like that, because the API may choose to free it or reuse it by returning it to a pool. The point is, it is an extremely bad programming practice to assume ownership of a resource that we did not create, unless we are specifically told otherwise.

Vijay Mathew
How does that sit with the likes of malloc() and strcmp()? They're meant to be freed at a different level. In fact, reductio ad absurdum, free() itself would never free memory :-)
paxdiablo
@paxdiablo, strcmp()? Did you mean strdup()? If so, I don't see how they are meant to be freed at a different level. strcmp(), of course, doesn't allocate anything.
Heath Hunnicutt
@HH, I don't think he really means it. The principle outlined in the answer (clean up your own mess) is sound. Avoid the construct used by one of my former colleagues, that his functions returned a char * to the (text) answer, in which case you had to free it, or to an error message, in which case you had not to free it.
Brian Hooper
I agree with paxdiablo. Also that principle doesn't make sense when for example designing an API where you'd need to allocate memory for some data structure and pass that back to a user to fiddle with. Wouldn't it be better to stick to the principle "always free memory in the same context as it was allocated", meaning that if you're allocating memory when in X (X meaning translation unit/part of api/program state) it most probably makes sense to also free that memory when in X. In short; if you're allocating in network_stuff.c and freeing in paint_rectangle.c it's time to redesign =).
manneorama
@Heath, yes, sorry, a bit of a brainfart there. I meant strdup. I tend to prefer the "responsibility for memory goes with the memory itself" school of thought. In other words, malloc and strdup pass back the memory _and_ the responsibility. When you pass a block to another thread, _don't touch it again_ until that thread gives back the responsibility. If an API function states that you must give it malloced memory which it will then free, the responsibility _never_ comes back to you (e.g., free).
paxdiablo
There seems to be a lot of confusion on my answer, someone even going to the extreme of stating that 'free() itself would never free memory'. So I added an example. What I said is just a simple, generally accepted practice followed when programming in languages that only have explicit memory management.
Vijay Mathew
The principle is a good one. It doesn't apply to strdup, malloc and free because they are part of the memory allocation infrastructure, and also they *document their behaviour*.
JeremyP
@paxdiablo - I do not understand when you write that `malloc` and `strdup` are meant to be freed "at a different level." Can you explain that?
Heath Hunnicutt
@Heath, the classic implementation of strdup is to call malloc and copy the string to the resultant memory block. If you dogmatically followed the "always free memory at the same level", you would never be allowed to return that memory from strdup since you'd have to clean it up and the same level. To say that that's ok since they document their behaviour is irrelevant since your own code can document its behaviour as well. Keep in mind this is not a bad answer, especially since Vijay has tempered the " _always_ free memory ..." with the phrase "unless we are told otherwise" (less dogmatic).
paxdiablo
The paradigm of passing a pointer back to the API for freeing is a good one since if you may want to introduce other cleanup code (more than just a free) at some point in the future. However, others subscribe to YAGNI, which would dictate the right time to allow for that is when it's needed, not in anticipation of the _possible_ need (in other words, change the API at the time when you need to do the extra stuff and clients will have to change their code as well). I just wanted to point out a counter-argument for the original answer. Both sides have their pros and cons.
paxdiablo
A: 

Sometime a API expects a allocated buffer and its upto to the owner of the function which calls that api to free it.

myFunc()
{
char *error = malloc(<max size of error string>);
foo(error);
//Free the pointer here
free(error);

}

Some API like GLIB api's expect pointer to address of a declared variable

myFunc()
{
GError *error;

glib_api(&error);
if (error)
{
printf("Error %s", error-> message);
// can use  glib API to free if error is NON NULL but message is allocated by GLIB API
g_error_free(error);
}
}

So even if you have not allocated memory to a variable you need to do the freeing while using standard libraries.

An allocated piece of memory, if not freed will result in lesser memory in a multiprocess environment, thereby degrading the performance of the system.

Praveen S
+1  A: 

It seems that you are used to OOP style. I don't like the OOP, and for me it would be weird if I'd obtain a copy of an object after assigning. In this case the string is somewhere in memory, and its address is sent as char*, and not the whole string. Also, be careful that you can free() only the pointers returned by malloc(), and only once.

ruslik
A: 

With plain char *, I would recommend always writing code with a policy that the caller "owns" the string and is responsible for freeing it if it was obtained by malloc. On the other hand, one could certainly envision "pseudo-pass by value" string objects in C, implemented as a struct, where policy dictates you have to relinquish ownership of a string (or duplicate it first and pass the duplicate) when passing strings as arguments. This could work especially well if the implementation used reference-counted storage for strings where the object passed was just a reference to the storage, so that the "duplicate" operation would merely be a reference-count increment plus trivial wrapper-struct allocation (or even pass-by-value struct).

R..