When it will be freed?

I

Ian Collins

Rafael said:
jameskuyper escreveu:


Sorry, but I don't think I get the "early return" meaning...

More than one return in a function. Doing so can introduce obscure bugs
where a resource is allocated earlier and released before the final
return, but leaked if not released before some early return.
 
J

jameskuyper

Rafael said:
jameskuyper escreveu:


Sorry, but I don't think I get the "early return" meaning...
What should I do? Write more fake code?

Let me clarify what I'm referring to by re-writing your code in the
"pure" style:

int main(int argc, char **argv)
{
char *whatever;
int retval = EXIT_SUCCESS;

if( (whatever = strdup(whatothereverchar))==NULL)
retval = docomplain();
else
{
/*handle whatever*/

free(whatever);
}

return retval;
}

The relevant mantra says that there shall always be only one exit
point from a function. This is supposed to make it easier to
understand the control flow of the function, and as result, make it
easier to avoid or detect incorrect control flow. While I understand
the point being made, I'm not fully in agreement with the proposed
solution. As is typical, re-writing to have only one exit point made
this code somewhat more complicated. It required addition of a named
variable, the creation of an else{} block, and a corresponding
indentation of the code in that block. In my experience, the extra
indentation is the biggest problem, because too much indentation can
render a function of only moderate size virtually unreadable. I
normally indent by 4 spaces; you seem to prefer 2 spaces, which makes
excess indentation less of a problem, but at the expense of making the
indentation harder to notice.

These disadvantages cancel out some of the supposed benefits of this
style.
 
R

Richard Tobin

jameskuyper said:
Structured programming purists would object to the early return from
main(). I'm not such a purist, though I recognize that there's some
validity in their objections to such code.

I recommend the opposite style: if a function has a "main line",
that is, a sequence that is followed in the normal case, then as
far as possible that sequence should be at the top level, with
exceptional cases inside conditional blocks. This naturally leads
to the use of early returns, and the occasional use of gotos to
handle common clean-up.

This applies mainly to long functions preforming a sequence of actions
with the possibility of errors along the way; the situation is very
different for functions handling a number of equally significant
cases.

-- Richard
 
R

Rafael

Stephen Sprunk escreveu:
Some programmers do not like the idea of multiple return points from a
function; they want all code paths to reach a single return point at the
end of the function. They often extend the same logic to main(),
basically banning the use of exit() and requiring all functions to
return so that (the first instance of) main() returns from a single point.

While there are some advantages to having a single return point, more
pragmatic programmers believe that complex code is often easier to
understand with multiple return (or exit()) points. This is not unlike
the debate about "goto"; avoiding it is a good idea in general, but
there are times it's the best tool for the job.

S

Ow... I see. So... what should I do if whatever return null? goto or
endless if's?
 
R

Rafael

Ian Collins escreveu:
More than one return in a function. Doing so can introduce obscure bugs
where a resource is allocated earlier and released before the final
return, but leaked if not released before some early return.

Ow... I see. So... what should I do if whatever return null? goto or
endless if's?
 
C

CBFalconer

tfelb said:
There are many string functions out there and many use malloc/calloc
mechanisms /but/ they aren't freed before the function returns? When
it will be freed? Because if I use strdup many times the function
malloc's every time more space from the heap or is my thesis wrong?

I saw many implementations of strdup and they all are similar

char *strdup(char *s1) {
char *newString;
newString = (char *)malloc(strlen(s1) + 1); /* I know casting
malloc isn't necessary */
strcpy(newString,s1);
return newString; /* no freeing */
}

Simply free the returned value when done with it. Treat strdup
(which you should write or copy, since it doesn't exits in standard
C) just like malloc.

if (sptr = strdup(thestring)) { /* checking for errors */
/* code to use and operate on sptr */
}
free(sptr);

strdup shouldn't copy the string if malloc failed.
 
I

Ian Collins

Rafael said:
Ian Collins escreveu:

Ow... I see. So... what should I do if whatever return null? goto or
endless if's?

Keep your functions short and this won't be an issue.
 
R

Rafael

jameskuyper escreveu:
Let me clarify what I'm referring to by re-writing your code in the
"pure" style:

int main(int argc, char **argv)
{
char *whatever;
int retval = EXIT_SUCCESS;

if( (whatever = strdup(whatothereverchar))==NULL)
retval = docomplain();
else
{
/*handle whatever*/

free(whatever);
}

return retval;
}

Ok, I see.

Honestly... I just like the early return way for that example, but I'll
try in other sources. :)
 
R

Rafael

Richard Tobin escreveu:
I recommend the opposite style: if a function has a "main line",
that is, a sequence that is followed in the normal case, then as
far as possible that sequence should be at the top level, with
exceptional cases inside conditional blocks. This naturally leads
to the use of early returns, and the occasional use of gotos to
handle common clean-up.

This applies mainly to long functions preforming a sequence of actions
with the possibility of errors along the way; the situation is very
different for functions handling a number of equally significant
cases.

-- Richard


Oww.... that's exactly my way. But (and there is always a catch) too,
I'm usual to development teams, where you need, mostly, to tick with
others clock.
 
C

Chad

Exactly. One of the common uses for strdup() is to "fix" functions that
return a pointer to a static buffer, so if strdup() did the same thing,
it'd be rather pointless.

How does using strdup() possibly fix functions that return a pointer
to a static buffer?
 
J

James Kuyper

Chad said:
How does using strdup() possibly fix functions that return a pointer
to a static buffer?

The work "fix" was in quotation marks for a reason. Such functions may
be doing precisely what they're supposed to do, so they don't really
need fixing. However, what they're supposed to do is inconvenient for
some purposes, because the same static buffer is used for multiple calls
to the function, and a program might still need access to the string
created by the first call, after the later calls have occurred. strdup()
provides an easy way to deal with that inconvenience:

char *ptime = strdup(asctime(time_pointer));
 
R

Richard

James Kuyper said:
The work "fix" was in quotation marks for a reason. Such functions may
be doing precisely what they're supposed to do, so they don't really
need fixing. However, what they're supposed to do is inconvenient for
some purposes, because the same static buffer is used for multiple
calls to the function, and a program might still need access to the
string created by the first call, after the later calls have
occurred. strdup() provides an easy way to deal with that
inconvenience:

char *ptime = strdup(asctime(time_pointer));

Is that long winded for it makes a copy since the static data buffer
might change?
 
A

Antoninus Twink

I vaguely recall an anecdote about a couple of engineering students
who were sharing a house. Each devoted a perhaps unwarranted amount of
time to considering the question of whether it was more practical to
hang toilet rolls with the dangling end on the wall side or the room
side. Each felt that he had approached the problem objectively,
rationally, and logically, with pragmatism being the sole criterion.
And of course they arrived at opposite answers.

It's the way you tell 'em, Dicky! Hilarious.
 
K

Kaz Kylheku

goto is a four letter word.

Just grepped the sources of a kernel module I wrote and maintain, as part of a
custom embedded Linux distro.

The goto construct occurs 39 times.

Put that in your pipe and smoke it.
 
A

Andrew Smallshaw

I recommend the opposite style: if a function has a "main line",
that is, a sequence that is followed in the normal case, then as
far as possible that sequence should be at the top level, with
exceptional cases inside conditional blocks. This naturally leads
to the use of early returns, and the occasional use of gotos to
handle common clean-up.

I agree. The case is even more pronounced with loops: I groan
whenever I see contorted loop control logic that considers every
corner case in the condition. It is simply a distraction which
makes it difficult to see the wood from the trees.

If a particular case needs special handling I like to see it handled
_and_then_forget_about_it_. Doing otherwise invariably means you
have large amounts of code indented more than they need to be.
When you eventually leave that indentation you have to go back and
remind yourself where the indentation came in, and it was for
something that you've dealt with earlier and don't want to be
bothered by again.

Structured programming should be your friend, not something to be
observed with religious fervour even when it doesn't make sense.
If you need to handle a particular case then a simple:

if (condition) {
/* appropriate action */

return; or break; or continue;
}

solves the problem. You don't need to consider that case again.
 
S

Stephen Sprunk

Chad said:
How does using strdup() possibly fix functions that return a pointer
to a static buffer?

Imagine such a function that you need to call several times in a row
with different arguments. Without strdup(), you can only use the
returned string until the next call modifies it. With strdup(), you can
easily make a copy, which will persist until you free() it.

Just the other day someone asked about a problem he was having with
inet_ntoa(); he called it twice and then tried to print the two
resulting strings, yet the string from the second call was showing up
twice. Using strdup() is the simplest solution to that problem.

This is obviously a hack, which is why I put "fix" in quotes. The
"correct" solution would be to fix the function so that it wrote the
result into a caller-provided buffer; however, when you are using
functions from someone else's library (in this case, Berkeley Sockets),
that is usually not an option, so strdup() is the best you can do to
work around the poor interface design.

S
 
K

Keith Thompson

Andrew Smallshaw said:
I agree. The case is even more pronounced with loops: I groan
whenever I see contorted loop control logic that considers every
corner case in the condition. It is simply a distraction which
makes it difficult to see the wood from the trees.

If a particular case needs special handling I like to see it handled
_and_then_forget_about_it_. Doing otherwise invariably means you
have large amounts of code indented more than they need to be.
When you eventually leave that indentation you have to go back and
remind yourself where the indentation came in, and it was for
something that you've dealt with earlier and don't want to be
bothered by again.

Structured programming should be your friend, not something to be
observed with religious fervour even when it doesn't make sense.
If you need to handle a particular case then a simple:

if (condition) {
/* appropriate action */

return; or break; or continue;
}

solves the problem. You don't need to consider that case again.

And then later, during maintenance, you realize that you need to
perform some action (releasing a resource, for example) before leaving
the function (or loop), regardless of whether the (condition) was true
or not. You can either duplicate the added code (and the two copies
will inevitably diverge over time), or you can change the structure.

Having said that, I actually like and use the idiom of testing for
unusual conditions and leaving early. I just have to watch out for
problems later on.

One possible solution is to use a goto (no, really) to jump to the
cleanup code:

void foo(void)
{
while (something) {
while (something_else) {
if (something_bad_happened) {
goto CLEANUP;
}
}
}
CLEANUP:
clean_things_up;
}

gotos are always a potential problem, but branching forward in the
code is far less dangerous than branching backward.
 
R

Rafael

Keith Thompson escreveu:
One possible solution is to use a goto (no, really) to jump to the
cleanup code:

void foo(void)
{
while (something) {
while (something_else) {
if (something_bad_happened) {
goto CLEANUP;
}
}
}
CLEANUP:
clean_things_up;
}

Would that work for cleaning?

#include <stdarg.h>

int cleaner(int counter, ...)
{
va_list ptr;
int i, rc;
void *pointer;

rc = EXIT_SUCCESS;

va_start(ptr, counter);
for (i = 0; i < counter; i++)
{
if((pointer = va_arg(prt, void*))!=NULL)
free(pointer);
else
rc = EXIT_FAILURE;
}

va_end(ptr);
return rc;
}

and you can free a lot of pointers in order.


Or maybe pass a va_list* instead of ...

int cleaner(int counter, va_list *ptr)
{
int i, rc;
void *pointer;

rc = EXIT_SUCCESS;

for (i = 0; i < counter; i++)
{
if((pointer = va_arg(ptr, void*))!=NULL)
free(pointer);
else
rc = EXIT_FAILURE;
}

va_end(ptr);
return rc;
}

And create a list on caller function, cleaning just what was assigned
(using counter as index - maybe working with counter value to debug free
problems)?

goto can just point to the execution of this function and clean the
house in one single step (or even the structured thing).

Of course, some function will be a demand to feed the va_list (I have no
clue of other way to do the "insert" into the list), but I think thats
more clean to handle then a void* array.

Maybe ugly, but clean.

I really don't have a compiler here to try that out, but the big picture
is probably easy to catch.


Rafael
 
T

tfelb

You know that goto's required by doped silicon are pretty good smokes

Hmm I did what someone said..I coded a strpad function with the
parameters (oldstring, newLen, given character) (make the orginal
string bigger (the newLen is now the len of the new string), copy the
old string into /new/ string and fill the rest with '.' chars.
But a heap corruption takes place?

1. In the function strpad, I terminate the new string with '\0'. Maybe
thats the cause?

One solution for the problem I guess is if i set p after the printf
call to NULL ( p = NULL )

int main(int argc, char **argv)
{

char text[] = "This is a test sentence);
char *p = strpad(text,30,'.');

if(p)
{
printf("%s",p);
free(p); / cause a memory heap corruption ? */
}



}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,776
Messages
2,569,602
Members
45,182
Latest member
BettinaPol

Latest Threads

Top