By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
435,106 Members | 2,335 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 435,106 IT Pros & Developers. It's quick & easy.

clean exit - suggestion - C version

P: n/a
Hello,

This question was asked in comp.lang.c++ and the answers involved the use of
objects whose destructors are automatically called when getting out of
scope, however I was expecting suggestions that doesn't involve the use of
objects. So here is the question again.

I have a function like:

void fnc() {
char *mem1, *mem2, *mem3, *mem4;
FILE *fp1;
// lots of code...
// mem1, 2, 3, 4 got allocated
// lots of code and condition checks
if (condition_failed)
{
// blah blah
// free mem1, mem2, mem3, mem4
return;
}

if (condition2_failed)
{
// blah blah
// free mem1, mem2, ...
fclose(fp1);
return;
}

// here the end of routine (clean exit code):
// free mem1, mem2, mem3, mem4
}

Usually, I would use compiler specific solution by putting most of the code
in __try() and the clean exit code in _finally() block then to reach the
clean exit code I would invoke __leave.

Or use lables and then goto clean_exit

Any better way, other than goto or compiler specific solution?

--
Elias
Nov 14 '05 #1
Share this Question
Share on Google+
10 Replies


P: n/a
"lallous" <la*****@lgwm.org> wrote in message
news:bt************@ID-161723.news.uni-berlin.de...
Hello,
...
I have a function like:

void fnc() {
char *mem1, *mem2, *mem3, *mem4;
FILE *fp1;
// lots of code...
// mem1, 2, 3, 4 got allocated
// lots of code and condition checks
if (condition_failed)
{
// blah blah
// free mem1, mem2, mem3, mem4
return;
}

if (condition2_failed)
{
// blah blah
// free mem1, mem2, ...
fclose(fp1);
return;
}

// here the end of routine (clean exit code):
// free mem1, mem2, mem3, mem4
}

Usually, I would use compiler specific solution by putting most of the code in __try() and the clean exit code in _finally() block then to reach the
clean exit code I would invoke __leave.

Or use lables and then goto clean_exit

Any better way, other than goto or compiler specific solution?


A never ending story... You will get that many replies as many posters there
are on Usenet ;-)

My prefered solution is:

void fnc (void)
{
char * const mem1 = something_that_allocates_mem1();
if (mem1)
{
char * const mem2 = something_that_allocates_mem2();
if (mem2)
{
/* ...etc. */
free(mem2);
}
free(mem1);
}
}

This has, IMO, two main advantages:
1. It's much clearer, variables are only declared where and when they are
actually used.
2. Memory is freed in the reverse order of allocation, which is usually
considered a good practice (though it should make no difference).

On the other hand, it may get a bit cumbersome if you have too many
variables to clear.

But as I said, it is all merely a matter of style. Use what you think is the
best (or your company coding guidelines dictate you).

Peter
Nov 14 '05 #2

P: n/a
Peter Pichler wrote:

"lallous" <la*****@lgwm.org> wrote in message
news:bt************@ID-161723.news.uni-berlin.de...
...
I have a function like:

void fnc() {
char *mem1, *mem2, *mem3, *mem4;
FILE *fp1;
// lots of code...
// mem1, 2, 3, 4 got allocated
// lots of code and condition checks
if (condition_failed)
{
// blah blah
// free mem1, mem2, mem3, mem4
return;
}

if (condition2_failed)
{
// blah blah
// free mem1, mem2, ...
fclose(fp1);
return;
}

// here the end of routine (clean exit code):
// free mem1, mem2, mem3, mem4
}
.... snip ...
A never ending story... You will get that many replies as many
posters there are on Usenet ;-)

My prefered solution is:

void fnc (void)
{
char * const mem1 = something_that_allocates_mem1();
if (mem1)
{
char * const mem2 = something_that_allocates_mem2();
if (mem2)
{
/* ...etc. */
free(mem2);
}
free(mem1);
}
}


and my version is:

void fnc(void)
{
char *cp1;
char *cp2;
char *cp3;
/* as needed */

if (!(cp1 = malloc(CP1SIZE))) goto cp1fail;
else if (!(cp2 = malloc(CP2SIZE))) goto cp2fail;
else if (!(cp3 = malloc(CP3SIZE))) goto cp3fail;
else {
/* they all worked - use em */
}
cp3fail:
free(cp3);
cp2fail:
free(cp2);
cp1fail:
free(cp1);
}

which is easily extended/compressed to the things needed. For
cleanup with free we can rework this to eliminate the gotos and
simply use NULL initializations for the cpN, but the above
organization will work with more complex scenarios.

--
Chuck F (cb********@yahoo.com) (cb********@worldnet.att.net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net> USE worldnet address!
Nov 14 '05 #3

P: n/a
CBFalconer <cb********@yahoo.com> wrote in message news:<40***************@yahoo.com>...
Peter Pichler wrote:

"lallous" <la*****@lgwm.org> wrote in message
news:bt************@ID-161723.news.uni-berlin.de...
...
I have a function like:

void fnc() {
char *mem1, *mem2, *mem3, *mem4;
FILE *fp1;
// lots of code...
// mem1, 2, 3, 4 got allocated
// lots of code and condition checks
if (condition_failed)
{
// blah blah
// free mem1, mem2, mem3, mem4
return;
}

if (condition2_failed)
{
// blah blah
// free mem1, mem2, ...
fclose(fp1);
return;
}

// here the end of routine (clean exit code):
// free mem1, mem2, mem3, mem4
}

... snip ...

A never ending story... You will get that many replies as many
posters there are on Usenet ;-)

My prefered solution is:

void fnc (void)
{
char * const mem1 = something_that_allocates_mem1();
if (mem1)
{
char * const mem2 = something_that_allocates_mem2();
if (mem2)
{
/* ...etc. */
free(mem2);
}
free(mem1);
}
}


and my version is:

void fnc(void)
{
char *cp1;
char *cp2;
char *cp3;
/* as needed */

if (!(cp1 = malloc(CP1SIZE))) goto cp1fail;
else if (!(cp2 = malloc(CP2SIZE))) goto cp2fail;
else if (!(cp3 = malloc(CP3SIZE))) goto cp3fail;
else {
/* they all worked - use em */
}
cp3fail:
free(cp3);
cp2fail:
free(cp2);
cp1fail:
free(cp1);
}

which is easily extended/compressed to the things needed. For
cleanup with free we can rework this to eliminate the gotos and
simply use NULL initializations for the cpN, but the above
organization will work with more complex scenarios.


My question was:
1)I have allocated lots of resources (memory, files, ...)
2)for some conditions other than allocation failures the code will
have to exit and clean up, but instead of cleaning up everytime and
repeating the clean up code, the clean up code must be written once
and called many times by some technique such as 'goto'

// here resources are allocated
// here some condition
if (failed)
{
// cleanup code
}
// some code
// some condition
if (failed2)
{
// same cleanup code as above.....
}
Nov 14 '05 #4

P: n/a
On Mon, 12 Jan 2004 22:00:37 -0800, lallous wrote:
Peter Pichler wrote:
> A never ending story... You will get that many replies as many
> posters there are on Usenet ;-)

:)
My question was:
1)I have allocated lots of resources (memory, files, ...)
2)for some conditions other than allocation failures the code will
have to exit and clean up, but instead of cleaning up everytime and
repeating the clean up code, the clean up code must be written once
and called many times by some technique such as 'goto'


Initialize all pointers to NULL.
Use true false value to indicate successful allocation of resources where
the handle itself (such as a file handle) can't be used to check if the
resource should be freed. If you detect an error use a goto.

This works for the simple cases, but I'd bet someone could come up with an
example where the method would fail.

int fn() {

char *mem1 = NULL;
char *mem2 = NULL;
char *mem3 = NULL;
char *mem4 = NULL;
FILE *fp1 = NULL;
int some_resource_flag = 0;

int result;

/* allocate resources, goto cleanup on failure */

cleanup:
if (some_resource_flag) free_some_resource();
if (fp1) fclose(fp1);
free(mem4);
free(mem3);
free(mem2);
free(mem1);

return result;

}
--
NPV

"the large print giveth, and the small print taketh away"
Tom Waits - Step right up

Nov 14 '05 #5

P: n/a
Nils Petter Vaskinn <no@spam.for.me.invalid> wrote in message news:<pa****************************@spam.for.me.i nvalid>...

I like all those solutions posted above, and have used them all
myself. What's not covered, however, is leaving the function the way
it is! :-D That's my current 'fave method'. Early programming exits
are supposedly a 'bad thing', but they occasionally have advantages
over the other methods when:

1. The cleanup code to be executed varies from exit to exit.
2. The remaining code is large, and doing it in one of the above
methods results in heavily indented code.

Using the other two methods, it's impossible to be consistent - it's
appropriate only occasionally. The 'early exit' method is useful more
frequently, but sometimes results in the redundant line or two.

In general, if early exits will give me what I want with no duplicate
code, or only one line, I'll use it. If the cleanup code is
significant, I use one of the methods outlined by the others. I prefer
the clean reading style you get with early exits, so I use it most
frequently. This is a change from my C coding style as a beginner.

//----------------------------------
// Here's an example of its use:
//----------------------------------
if (!A)
return; // error condition 1
if (!B)
return; // error condition 2
if (!C)
return; // error condition 3.

// If the code got here, it passed all the tests.
dofunction(D);
//----------------------------------
// Without it, the code would look like:
//----------------------------------
if (A)
if (B)
if (C)
dofunction(D);
else
return;
else
return;
else
return;

In this trivial example, the 'else returns' are optional and can be
left off... but if they're supposed to do cleanup, you can see where
this indentation could get quite large. Also, with this method, it's
easy to lose track of where you are in the 'if' hierarchy, and
inadvertantly execute some logic you weren't supposed to. I haven't
had that problem since switching to the 'early exit' style of
programming.
--Kamilche
Nov 14 '05 #6

P: n/a
On Tue, 13 Jan 2004 02:36:43 -0800, Kamilche wrote:
Nils Petter Vaskinn <no@spam.for.me.invalid> wrote in message
news:<pa****************************@spam.for.me.i nvalid>...

I like all those solutions posted above, and have used them all myself.
What's not covered, however, is leaving the function the way it is! :-D
That's my current 'fave method'. Early programming exits are supposedly
a 'bad thing', but they occasionally have advantages over the other
methods when:

1. The cleanup code to be executed varies from exit to exit. 2. The
remaining code is large, and doing it in one of the above methods
results in heavily indented code.
1. My suggestion to have a flag for each resource to indicate if it should
be freed or not will usually eliminate the need for different cleanups.
Since checking the flags takes care of the different cases

2. The goto method I described above shouldn't increase the indentation
level significantly unless you use a very bizarre style.

Using the other two methods, it's impossible to be consistent - it's
appropriate only occasionally. The 'early exit' method is useful more
frequently, but sometimes results in the redundant line or two.

In general, if early exits will give me what I want with no duplicate
code, or only one line, I'll use it. If the cleanup code is significant,
I use one of the methods outlined by the others. I prefer the clean
reading style you get with early exits, so I use it most frequently.
This is a change from my C coding style as a beginner.


[snip example]

Your early exit exaple doesn't really fit in this discussion since there
is no cleanup.

But "return;" and "return something;" can easily be replaced with

/* result = something if there is a return from the function */
goto cleanup;

If there was any cleanup to be done, so the body of the code would
essentially be the same as with early exit. But you avoid having the
cleanup code at every exit point.

What it all boils down to is trying to make the code easy to understand
and maintain. Your example is, so it's perfectly ok code (IMO) but I would
try to avoid early exit in some other cases (like in one single case
hidden deep within nested ifs and loops where a quick glance at the
function could lead one to believe it has only a single exit point.
--
NPV

"the large print giveth, and the small print taketh away"
Tom Waits - Step right up

Nov 14 '05 #7

P: n/a
lallous wrote:
CBFalconer <cb********@yahoo.com> wrote in message news:

.... snip ...

and my version is:

void fnc(void)
{
char *cp1;
char *cp2;
char *cp3;
/* as needed */

if (!(cp1 = malloc(CP1SIZE))) goto cp1fail;
else if (!(cp2 = malloc(CP2SIZE))) goto cp2fail;
else if (!(cp3 = malloc(CP3SIZE))) goto cp3fail;
else {
/* they all worked - use em */
}
cp3fail:
free(cp3);
cp2fail:
free(cp2);
cp1fail:
free(cp1);
}

which is easily extended/compressed to the things needed. For
cleanup with free we can rework this to eliminate the gotos and
simply use NULL initializations for the cpN, but the above
organization will work with more complex scenarios.


My question was:
1)I have allocated lots of resources (memory, files, ...)
2)for some conditions other than allocation failures the code will
have to exit and clean up, but instead of cleaning up everytime and
repeating the clean up code, the clean up code must be written once
and called many times by some technique such as 'goto'


which is just what my pattern does. malloc/free are just one
examples of routines that setup and cleanup.

if (setupfails(params1)) goto clean1;
else if (setupfails(params2)) goto clean2;
else {
/* success */
}
clean2: cleanup(params1);
clean1: cleanup(params2);
--
Chuck F (cb********@yahoo.com) (cb********@worldnet.att.net)
Available for consulting/temporary embedded and systems.
<http://cbfalconer.home.att.net> USE worldnet address!
Nov 14 '05 #8

P: n/a
> void fnc() {
char *mem1, *mem2, *mem3, *mem4;
FILE *fp1;
// lots of code...
// mem1, 2, 3, 4 got allocated
// lots of code and condition checks
if (condition_failed)
{
// blah blah
// free mem1, mem2, mem3, mem4
return;
}

if (condition2_failed)
{
// blah blah
// free mem1, mem2, ...
fclose(fp1);
return;
}

// here the end of routine (clean exit code):
// free mem1, mem2, mem3, mem4
}


I have a method I use only when I feel it really clarifies the situation,
and I always comment it (with a few words) when I'm doing so. It assumes
that all pointers (or objects in your case) are set to such a state that
cleaning them when left untouched isn't a problem or that it is obvious when
they don't need to be cleaned.

char *mem1 = NULL, *mem2 = NULL, *mem3 = NULL;
/* or you could do this:
char *mem1, *mem2, *mem3;
mem1 = mem2 = mem3 = NULL;
*/

/* try-like exception construct */
do
{
mem1 = malloc(MEM1SIZE);
if ( !mem1 )
break;

/* do something here */

mem2 = malloc(MEM1SIZE);
if ( !mem2 )
break;

/* do something else here */

mem1 = malloc(MEM1SIZE);
if ( !mem3 )
break;

/* do even more here */
}
while ( 0 );

if ( mem1 ) free(mem1);
if ( mem2 ) free(mem2);
if ( mem3 ) free(mem3);

return;

The do { } while ( 0 ) will always be executed once. You could put a return
code in there if you want to as well.

I think it is a decent solutions, much like the goto solution, but the
reason I prefer this over the goto approach is because it is very obvious
what block of code is being "tried" and it bears a little more resemblance
to a real try/catch construct.

Hope this helps,

Martijn Haak
http://www.sereneconcepts.nl
Nov 14 '05 #9

P: n/a
Nils Petter Vaskinn <no@spam.for.me.invalid> wrote in message news:<pa****************************@spam.for.me.i nvalid>...
What it all boils down to is trying to make the code easy to understand
and maintain. Your example is, so it's perfectly ok code (IMO) but I would
try to avoid early exit in some other cases (like in one single case
hidden deep within nested ifs and loops where a quick glance at the
function could lead one to believe it has only a single exit point.


Yeah, consistency with the rest of the code is important! I use early
exits very frequently in a common set of modules, so when you're
reading it, exceptions are pulled out and 'legal code' is in a
straight line down the page. But if the code didn't use early exits so
heavily, I would hesitate to include it - it's best to follow the
convention of the existing codebase.

I read about a study they did on experienced programmers versus newbie
programmers. As long as the code did 'the expected thing' and followed
a convention of some sort, the experienced programmers were far more
productive. But if the code did it one way in this module, another way
in another module, and was relatively a mishmash, they were just as
unproductive as the newbie programmers.

Far from being the 'hobgoblin of little minds', consistency extends a
programmer's brainpower. Use it in the style most suited to YOUR
brain. :-)

--Kamilche
Nov 14 '05 #10

P: n/a
"Martijn" <su*********************@hot-remove-mail.com> wrote in message
news:40*********************@news.xs4all.nl...
void fnc() {
char *mem1, *mem2, *mem3, *mem4;
FILE *fp1;
// lots of code...
// mem1, 2, 3, 4 got allocated
// lots of code and condition checks
if (condition_failed)
{
// blah blah
// free mem1, mem2, mem3, mem4
return;
}

if (condition2_failed)
{
// blah blah
// free mem1, mem2, ...
fclose(fp1);
return;
}

// here the end of routine (clean exit code):
// free mem1, mem2, mem3, mem4
}
I have a method I use only when I feel it really clarifies the situation,
and I always comment it (with a few words) when I'm doing so. It assumes
that all pointers (or objects in your case) are set to such a state that
cleaning them when left untouched isn't a problem or that it is obvious

when they don't need to be cleaned.

char *mem1 = NULL, *mem2 = NULL, *mem3 = NULL;
/* or you could do this:
char *mem1, *mem2, *mem3;
mem1 = mem2 = mem3 = NULL;
*/

/* try-like exception construct */
do
{
mem1 = malloc(MEM1SIZE);
if ( !mem1 )
break;

/* do something here */

mem2 = malloc(MEM1SIZE);
if ( !mem2 )
break;

/* do something else here */

mem1 = malloc(MEM1SIZE);
if ( !mem3 )
break;

/* do even more here */
}
while ( 0 );

if ( mem1 ) free(mem1);
if ( mem2 ) free(mem2);
if ( mem3 ) free(mem3);

return;

The do { } while ( 0 ) will always be executed once. You could put a return code in there if you want to as well.

I think it is a decent solutions, much like the goto solution, but the
reason I prefer this over the goto approach is because it is very obvious
what block of code is being "tried" and it bears a little more resemblance
to a real try/catch construct.

Hope this helps,

Martijn Haak
http://www.sereneconcepts.nl

Martijn, I like your do/while approach, it really looks like the
try/leave/finally block to me!

Thanks to all who posted their suggestions they were also useful in a way or
another.
--
Elias
Nov 14 '05 #11

This discussion thread is closed

Replies have been disabled for this discussion.