The D Programming Language

Dave Burton daveb at laidbak.UUCP
Thu Mar 10 15:25:08 AEST 1988


Sorry if this is a repost. The first one escaped while I wasn't looking :-).
-- (how fast is a cancel, anyway?) --

In article <25387 at cca.CCA.COM> g-rh at CCA.CCA.COM.UUCP (Richard Harter) writes:
|In article <1354 at laidbak.UUCP> daveb at laidbak.UUCP (Dave Burton) writes:
|>	int flag, foobar();
|>	x = foobar(&flag);
|>	...
|>	int foobar(arg)	int *arg;
|>	{ *arg = 1; return 0; }
|And this is what I was alluding to when I referred to playing games with
|pointers.  In the call statement you have to add an &; in each reference
|in the routine you add a *.  You have to also select one item as a 
|distinguished item which is returned by the function among those that
|are returned.

I don't think this would usually be a problem. Of the set of functions that
_must_ return more than one value, a healthy percentage are also modifying
a parameter, so the variable will be passed anyway. Of the remaining set,
the returned data is usually logically related, so arguably an aggregate of
some type should be used. In my opinion, this leaves only a small set of
functions which _must_ return more than one value. I do not deem this to
be of enough value to change a language (since D is supposedly a successor).

|But it is a kludge, and it is prone to error.  Throughout the
|code you "mean" flag, but sometimes you write &flag, and sometimes you
|write *flag, depending on where you are. 

When actually writing the code, I think the proper form would be clear
from the context. Of course, somebody will post a winner of the
obfuscated C contest to disprove this statement :-). Quite seriously,
I don't believe this to be a problem in _well structured code_. If
your functions are well defined, and they are presented clearly in your
code, neither the coder nor the maintenance programmer will have much
trouble understanding the differences. Such code will also use the power
of separate compilation, so there is a good chance these routines will
be in separate modules, further eliminating confusion.

|A more subtle point is that there is a difference in semantics between
|returning a list and passing things through the calling sequence.  A
|returned value is private unless it is explictly declared as a global;
|an item passed through a calling sequence may be a global and the called
|routine does not know this.  For example,
|FILE 1:
|	int x;
|	foo () { x = 2; baz(x,&x); }
|FILE 2:
|	extern x;
|	baz(in,out) int in, *out;
|	{ *out = 1; *out = *out + x; }
|is quite different from the same code where baz is a function returning
|a value which is declared internally.

Your point is well taken. I think the example is somewhat contrived, though.
Good program design would eliminate the need(?) for this type of construction.

|Call by reference is subject to
|unintended aliasing, whether it be real call by reference or simulated
|call by reference.

Aliasing is not limited to function calls (notice I didn't say unintended
aliasing). List return types could save a programmer from some unintentional
aliasing problems, and this is a Good Thing. But it doesn't really solve the
problem in the first place. Neither does noalias :-).

A further note on list returns:
Since there is no explicit return variable matching between caller and
callee, what would stop the programmer from committing a more serious error,
namely reversing or (for more than two returns), switching the returned values
around. I rather think this would open a whole new can of worms (how many
times have you put the parameters in improperly on a function call?). Think of
the additional time spent looking up references to functions, not only to get
the parameters right, but also the multiple return values (now, which order
did they come in?).

A win for passing pointers vs. aggregates: (this does relate - list returns
will usually not pass pointers around, but rather entire objects).
Then there is the issue of speed and efficiency. 
I resist the temptation to put structures on the stack when passing
parameters. The overhead involved in placing the thing on the stack,
operating on it there, then removing it upon return (after copying the
values back off the stack) can be large. A pointer to the aggregate works
very well, and it is much more likely the machine will have hardware support
for handling a pointer. This is both a time and a space win. Moreover, this
treatment (passing a pointer) of structs is in keeping with C's treatment
of another aggregate, arrays. Treating like classed objects similarly is
a win in the clarity and confusion department.

|Finally, there is one other annoying consequence of the pointer hack.
|You can run into pointer to 0 dereferencing problems if you aren't
|careful.  You shouldn't, but you can.

But this is present wherever pointers are, so in this context it does
not really add to your argument.
-- 
--------------------"Well, it looked good when I wrote it"---------------------
 Verbal: Dave Burton                        Net: ...!ihnp4!laidbak!daveb
 V-MAIL: (312) 505-9100 x325            USSnail: 1901 N. Naper Blvd.
#include <disclaimer.h>                          Naperville, IL  60540



More information about the Comp.lang.c mailing list