Jean-Claude Arbaut said:
A good practice (I think, I'm not a guru): a function never
returns a block it has allocated. This means that the caller
never frees memory, and the callee always frees all its
allocated blocks before function returns. If the callee needs
to return a block, then the caller allocates it and pass it
as an argument. Maybe there are situations where it doesn't work ?
An approach I've found useful is to write pairs of
functions: one function allocates and initializes memory
for a Whatever and returns a Whatever*, while the other
accepts a Whatever* argument and does whatever is needed
to "de-initialize" the Whatever, including releasing its
memory. The advantage is that the caller never needs to
the details of how a Whatever is built: how much memory
is needed, whether it's all in one chunk or is built from
several pieces linked together, and so on.
For example, consider fopen() and fclose(). As the
caller you do not need to know anything about what a `FILE'
looks like; all you care about is the `FILE*'. You do not
know whether the `FILE' is allocated dynamically or statically,
what extra buffers and such may be allocated along with it,
and so on -- all you need to know is that fopen() produces a
`FILE*', and that fopen() cleans it up when you're done.
The same pattern works well for "purely memory" constructs,
too. I've written a little expression evaluator that has
three (principal) functions in its interface: a compiler that
transforms the source expression into an "opaque" data type
that's allocated during compilation, an evaluator that takes
the opaque pointer and an array of user-supplied variable
values and returns the expression's value, and a destructor
that accepts the opaque pointer and discards the memory it
uses. The current version of the destructor is fairly simple:
void ExprDestroy(Expr *expr) {
free (expr);
}
.... but by packaging it where the caller can't see the details
I retain the freedom to change my mind about the way memory is
managed, and perhaps do something like
void ExprDestroy(Expr *expr) {
free (expr->constants);
free (expr->bytecodes);
#ifndef NDEBUG
free (expr->debugging_info);
#endif
free (expr);
}
The caller never needs to know how things are done "behind the
curtain."