[comp.lang.c] Function returning Structure: How does it work?

m100-2ai@WEB.berkeley.edu (05/29/90)

Hi everyone.
The following seems to be an "easy" question.  However, after thinking
about it, I find that I don't really understand how it really works...

Assume I have the following structure defined...

typedef struct STRUCT	STRUCT;
struct STRUCT
{
	int num;
	/* Other fields */
};

in ANSI C, I can define a function returning STRUCT, such as the following:

STRUCT function(void)
{
	STRUCT structure;

	structure.num = 0;
	/* Set other fields as well */
	return structure;	
}

Now, it seems that the storage space for 'structure' has to be allocated
on the heap (not on stack).  However, wouldn't this create a lot of
garbages you won't be able to free?  For example:

boolean is_something(STRUCT s)		{ return s.num * ....; }

And the call:

	if (is_something(function())) {
		printf("true.");
	}

will create garbage because the we cannot access the 'structure' returned
by function, and since it is allocated on the heap, it won't go away.
Essentially, it is garbage.

However, what strikes me more is that there seems to be no way you
can free that garbage.  I can try to do the following:

	STRUCT *ptr;

	ptr = &(function());	/* This may, or may not work, depending on */
				/*  your compiler. */

	if (is_something(*ptr)) {
		printf("true.");
	}

	free(ptr);		/* Again, may or may not work. */

As a matter of fact, there seems to be NO WAY you can manually free
the heap storage claimed by 'structure'.  

I don't think my assumption about garbage is correct.  However, I
just can't think of how the C-compiler actually handles it.  Can 
some one please disclaim/confirm what I have said.  Or even better,
can some one please enlighten me to show me how this is really done.

Any help or suggestion is GREATLY appreciated.

  __         ___      ___   _/'     Conrad Wong  (cwong@cory.berkeley.edu)
 /  \     __/   \----'   \-' c`-o
|    |   /  > __/_   / __/_`,  _|     Imitation is the sincerest form of
     \__/   \_____\`--\____\ ;/'       lack of imagination -- Ghondahrl

dkeisen@Gang-of-Four.Stanford.EDU (Dave Eisen) (05/29/90)

In article <1990May28.215331.29333@agate.berkeley.edu> m100-2ai@WEB.berkeley.edu () writes:
>
>STRUCT function(void)
>{
>	STRUCT structure;
>
>	structure.num = 0;
>	/* Set other fields as well */
>	return structure;	
>}
>
>Now, it seems that the storage space for 'structure' has to be allocated
>on the heap (not on stack). 

Why?

C implementations usually allocate automatic variables on the stack, the fact
that it is a structure has nothing to do with this. C is not known for playing
with the heap without the user telling it to.

The only problem lies in returning a struct value. In an implementation that
returns function values in a register, something special must be done
to handle return values larger than will fit in that register. One way to do
this is to return the address of a block of static memory where the struct can 
be found and have the calling function copy the value found there into the struct
the return value of the function is supposed to be assigned to. Of course, this
can lead to a great deal of copying so most of the time you're better off
arranging things for the function to have type STRUCT * instead.




--
Dave Eisen                      	    Home: (415) 323-9757
dkeisen@Gang-of-Four.Stanford.EDU           Office: (415) 967-5644
1447 N. Shoreline Blvd.
Mountain View, CA 94043

pfalstad@phoenix.Princeton.EDU (Paul John Falstad) (05/29/90)

In article <1990May28.215331.29333@agate.berkeley.edu> m100-2ai@WEB.berkeley.edu () writes:
>in ANSI C, I can define a function returning STRUCT, such as the following:
>
>STRUCT function(void)
>{
>	STRUCT structure;
>
>	structure.num = 0;
>	/* Set other fields as well */
>	return structure;	
>}
>
>Now, it seems that the storage space for 'structure' has to be allocated
>on the heap (not on stack).  However, wouldn't this create a lot of

No, that's not how it's done.  I thought it was handled by having the
whole structure pushed on the stack, but it turns out that's not how its
done either.

I tried this program with Amiga Manx 5.0a:

struct foo { int a,b; };
struct foo fooer(void)  { struct foo bar = {1,2}; return bar; }
main()
{
struct foo bar;

  bar = fooer();
}

Manx compiled it the same way as the following:

struct foo fooer(struct foo *ptr)
{
struct foo bar = {1,2};

 *ptr = bar;
 return ptr;
}
main()
{
struct foo bar;

  fooer(&bar);
}

So that's one way to do it.  I tried this gcc on a VAX, and it handled it by
returning the structure in r0 and r1.  I made the structure much bigger
(too big to fit in registers), and gcc compiled it pretty much the same
way as above.

-- 
Paul Falstad  PLINK:Hypnos GEnie:P.FALSTAD net:pfalstad@phoenix.princeton.edu
Disclaimer: My opinions, which belong to me and which I own, are mine.
-Anne Elk (not AN elk!)  The sun never set on the British empire because
the British empire was in the East and the sun sets in the West.

henry@utzoo.uucp (Henry Spencer) (05/29/90)

In article <1990May28.215331.29333@agate.berkeley.edu> m100-2ai@WEB.berkeley.edu () writes:
>...in ANSI C, I can define a function returning STRUCT...
>Now, it seems that the storage space for 'structure' has to be allocated
>on the heap (not on stack).  However, wouldn't this create a lot of
>garbages you won't be able to free? ...

It would, but that's not how it's done.  Returning a struct value *is*
tricky, and compilers use a variety of methods, but allocating heap space
is generally not acceptable, and allocating space that doesn't get freed
is definitely not acceptable.  The best method is for the caller, knowing
it's calling a struct-valued function, to allocate some space for the
return value, and either do this in a known place or else pass an extra
parameter which is a pointer to the space.  Then the called function just
puts the return value there.  This does fall down badly if the user does
not bother to declare that he's calling a struct-valued function, but
that comes under the heading of "if you lie to the compiler, it will get
its revenge".
-- 
As a user I'll take speed over|     Henry Spencer at U of Toronto Zoology
features any day. -A.Tanenbaum| uunet!attcan!utzoo!henry henry@zoo.toronto.edu

meissner@osf.org (Michael Meissner) (05/30/90)

In article <18222@well.sf.ca.us> rld@well.sf.ca.us (Rick Davis) writes:

| In article <1990May28.235336.5338@Neon.Stanford.EDU>, Dave Eisen writes:
| >
| > Of course, this
| > can lead to a great deal of copying so most of the time you're better off
| > arranging things for the function to have type STRUCT * instead.
| > 
| 
| I can't agree with this too strongly.  Passing entire structures back
| and forth is almost always a serious waste of time and stack space.
| Indeed, there are circumstances where it is necessary, (i.e communications)
| but most of the time you're much better off with the structure pointer.
| Also, some implementations of C won't even let you try.  Code portability
| alone works for me.

But a clever compiler could optimize:

	struct foo func(), var;

	var = func();

and pass the address of var (assuming it returns structures by passing
a pointer as a hidden argument), so that a temporary and a structure
copy are not required.
--
Michael Meissner	email: meissner@osf.org		phone: 617-621-8861
Open Software Foundation, 11 Cambridge Center, Cambridge, MA

Catproof is an oxymoron, Childproof is nearly so

karl@haddock.ima.isc.com (Karl Heuer) (05/31/90)

In article <8048@crdgw1.crd.ge.com> larocque@jupiter.crd.ge.com (David M. LaRocque) writes:
>I think I must be missing something fundamental in the way
>storage allocation works.  I see how malloc() can return 
>"permanant" storage in memory that the caller knows won't
>get overwritten until free() is called, at least I think I 
>understand the K&R II description of their malloc routine (pg 185).
>However, in the above approach, how can a caller be sure that
>makepoint() returned memory that won't get trounced somehow?

The usual implementation is that the caller supplies the storage space.

>In addition if a point is no longer needed is there a method
>analagous to free() that can be used to return the point's
>memory to the operating system?

No need.  The caller knows when it's done with it (usually by the end of the
next operator).  Note that in
	struct point p;
	p = make_point(x, y);
the contents are being copied, not the address, so the temporary storage may
be reclaimed immediately afterwards (regardless of the lifetime of `p').

Karl W. Z. Heuer (karl@ima.ima.isc.com or harvard!ima!karl), The Walking Lint

steve@taumet.COM (Stephen Clamage) (05/31/90)

In article <8048@crdgw1.crd.ge.com> larocque@jupiter.crd.ge.com (David M. LaRocque) writes:
>One instance I thought of that one may want to use a function that
>returns a structure is as an alternative to malloc.
>
>struct point { int x; int y; };
>struct point makepoint(int x, int y);
>struct rect { struct point pt1; struct point pt2; };
>struct rect screen;
>screen.pt1 = makepoint(0, 0);

In this example, the return from makepoint() is copied to a static
(in this case global) variable.  Following the call to makepoint, what
it actually returned is irrelevant, since it can no longer be referenced.
The copy stays where it is (in this case, globally available until the
end of the program).

If you had a local struct assigned the return from makepoint(), that
local struct would contain the value until it's containing function
returned, at which time it would disappear.

Think about a function returning an int.  Where is the int located?  Can
it be clobbered later?  Any function return value must be used (copied)
immediately as part of the expression calling the function.  This is as
true for structs as for ints.
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com

siegel@stsci.EDU (Howard Siegel) (06/01/90)

Conrad Wong (cwong@cory.berkeley.edu) writes:
>The following seems to be an "easy" question.  However, after thinking
>about it, I find that I don't really understand how it really works...
>
>   [simple stuct declaration and function returing struct...]
>
>Now, it seems that the storage space for 'structure' has to be allocated
>on the heap (not on stack).  However, wouldn't this create a lot of
>garbages you won't be able to free?  For example:
>
>   [other stuff...]
>
>I don't think my assumption about garbage is correct.  However, I
>just can't think of how the C-compiler actually handles it.  Can 
>some one please disclaim/confirm what I have said.  Or even better,
>can some one please enlighten me to show me how this is really done.

I can not say that all C compilers work this way, but on VMS (any version)
this is the story.  (Note that this is (should) be the same for all
languages that adhere to DEC's Common Calling Convention which is
documented someplace in the umpteen feet of manuals).  At least for
the example in question, I have tried it out and looked at the machine
code created by the VAX C compiler.

For functions that return a simple (atomic?) data type, the returned
value is put into register 0 if it is 4 bytes or smaller or into the
register 0/1 pair if it is 5-8 bytes big.  If the value to be returned
is bigger than 8 bytes then the compiler shifts the argument list
over and creates a new first argument which is a pointer to an object of
the proper data type.

Thus, if the function in question is defined in the code as:

    STRUCT function(int number)

the compiler will create code as if the function were defined as:

    void function(STRUCT *structure, int number)

In the calling routine, the compiler creates a temporary variable for
the return value on the stack (not the heap) and uses that variable in
the actual function call argument list.

Again, this is just how it happens on VMS and with the VAX C (not quite
ANSI) compiler.  Your mileage with other OS's will vary, but I would
imagine they'll act similarly.

(Sorry I could not provide compiler listings with the machine code
in them, but the link to the VMS machine is flaky at the moment).
-- 
    Howard Siegel    (301) 338-4418
    TRW   c/o Space Telescope Science Institute   Baltimore, MD 21218
    ARPA:  siegel@stsci.edu          SPAN:  STOSC::SIEGEL
    uucp:  {arizona,decvax,hao}!noao!stsci!siegel

scott@bbxsda.UUCP (Scott Amspoker) (06/06/90)

In article <18222@well.sf.ca.us> rld@well.sf.ca.us (Rick Davis) writes:
>> Of course, this
>> can lead to a great deal of copying so most of the time you're better off
>> arranging things for the function to have type STRUCT * instead.
>
>I can't agree with this too strongly.  Passing entire structures back
>and forth is almost always a serious waste of time and stack space.

...unless the structure can fit in a single word.  I've seen compilers
which handle that rather nicely.

>Also, some implementations of C won't even let you try.  Code portability
>alone works for me.

We been porting our C code to dozens of different platforms and C compilers 
for the *past 5 years* and have never encountered a C compiler that did
not allow structure assignment.  Much older compilers, however, might
complain.  Such compilers typically conform to old K&R and also don't
maintain separate name spaces for structure field names.  That's asking
too much for portability IMHO.


-- 
Scott Amspoker
Basis International, Albuquerque, NM
(505) 345-5232
unmvax.cs.unm.edu!bbx!bbxsda!scott