In article <11*********************@t46g2000cwa.googlegroups. com>,
<st*********@gmail.comwrote:
>All
Beginner/Intermediate level question. I understand that returning
ptr to local stack vars is bad.
Yes. The reason for that is that the local variable goes away when the
function returns, so you're left with a pointer that no longer points
at anything.
(Note also that the "stack" you refer to need not look anything like
what you probably think it looks like[1].)
Is returning foo_p_B from fnB() reliable all the time, so that using
foo_p_A does not break?
[Code kept below for reference]
Yes, assuming that it's not NULL (which you don't seem to be checking
for). The pointer that fnB returns originally came from malloc, and
anything you get from malloc will exist until you free it.
The thing to remember here is that a pointer is a /value/ that tells
you where to find an /object/. As long as it's pointing at something
that still exists (that is, the the pointer value refers to an object
that hasn't disappeared), you can pass that value up, down, or sideways
through the call stack with wild abandon, and you can use it to access
the object it refers to anywhere; but when the object it refers to stops
existing (for example, when the function invocation that created a local
variable returns, or when the memory obtained from malloc() is given to
free()), you can't use the pointer value anymore either.
(Note that the more different places you've put any particular pointer
value, the harder it is to make sure that all of those places know when
the object it's pointing at stops existing, so in general it's a good
idea to abandon abandon and pass pointer values around with great care.)
As a side note, your malloc call looks like this:
foo_p_C = (struct foo*) malloc(...);
(and there's a good chance that the "..." part includes "sizeof(struct
foo)" somewhere).
It would be a good idea to get in the habit of writing your mallocs
like this instead:
foo_p_C = malloc(nelems * sizeof *foo_p_C);
The void * that malloc returns will be converted to the appropriate type
by the compiler (if it complains, that means either you haven't told the
compiler what type malloc returns and you should #include <stdlib.h>
or you're using a C++ compiler and you should be using new instead of
malloc), and using "sizeof *pointer_on_left_side_of_assignment" saves
you the trouble of looking up which type it is (and saves you the trouble
of changing all your mallocs if you change the type).
fnA()
{
struct foo* foo_p_A;
int a=1, b=2;
foo_p_A = fnB( a, b);
/* Will this work all the time? */
foo_p_A->xxx
}
struct foo*
fnB( int a, int b)
{
struct foo* foo_p_B;
foo_p_B = fnC(a,b);
return foo_p_B;
}
struct foo*
fnC(int c, int d)
{
struct foo* foo_p_C;
....
foo_p_C = (struct foo*) malloc(...);
...
return foo_p_C;
}
dave
[1] All that's actually required of it is that it can store and manage
function invocation records in a stack-like fashion. Your
implementation probably uses a contiguous block of memory that it
indexes with a register to keep track of where it is, but there have
been real implementations that have done it in heap-allocated blocks,
and there's nothing other than common sense and lack of creativity
that prevents an implementation from putting them in a cave somewhere
in Antarctica.
--
Dave Vandervies dj******@csclub.uwaterloo.ca
I'd abandon abandon if I were you, and jump from record to record with
extreme care. --Richard Heathfield and Bill
The requirements call for abandon, so abandon it has to be. Godfrey in CLC