[net.lang.c] void casts; and C definition question

kendall@wjh12.UUCP (Sam Kendall) (10/02/83)

The complaint is that
	(void) f();
is a kludge, that
	(*(void (*)()) f)();	/* or something like that */
is the "real" way to do the former.  Well, I there is nothing wrong
with the former.  A void cast means "throw away a value".

An interesting point here is that the latter is not even guaranteed
to work in the C language.  Calling a function with an incorrectly
specified type usually works, but on some compilers (National
Semiconductor's 16032 C compiler, for instance), if f() returns a
structure, then it must be declared to do so for a call to work,
since the address of where to put the return value is passed as the
first arg of the function.

Many programs do not declare the return type of functions correctly;
for instance, it is common to leave off a
	char *strcpy();
definition if the return value is never used.  My question is this:  
What should the language say about calling functions with incorrectly
declared return values?  We cannot guarantee that it will work with
functions returning structures.  But should it otherwise?  Many programs
(unfortunately) depend on it.

	Sam Kendall			{allegra,ihnp4}!wjh12!kendall
	Delft Consulting Corporation	  decvax!genrad!wjh12!kendall

guy@rlgvax.UUCP (Guy Harris) (10/03/83)

The language should *not* guarantee that calling a function without having
properly declared the type of the function's return value should work, because
not all implementations can guarantee this.  If pointers and "int"s are
different sizes (as they are on several 68000 implementations, on Zilog's
segmented Z8000 implementation (I assume), and probably on several other
machines with a decently-sized address space but either weak support for
32-bit integers (like the 68K with its lack of 32-bit mul/div) or performance
losses for 32-bit integers) if you don't say "char *malloc()" or whatever
you are *guaranteed* to lose.  Programs which depend on this working should
either be changed not to or be hacked on such implementations to use "long"s
instead of "int"s.

	Guy Harris
	{seismo,mcnc,we13,brl-bmd,allegra}!rlgvax!guy

tim@unc.UUCP (Tim Maroney) (10/05/83)

The cited use of the (void) type cast is indeed incorrect if the operator
precedence rules of Kernighan and Ritchie are correct.  The example given
went like:

(void) foo(x);

to show that nothing important is returned from foo.  In fact, the
parentheses of the function call argument list group first, meaning that the
value returned by foo is a function of type void.  To do it right, you would
have to say:

((void) foo)(x);

I suppose that the rather sloppy definition of void in the public
documentation would allow you to make a weak case for the former being
correct, though.  Still, special properties of void notwithstanding, only
the latter form is correct.

Isn't C wonderful?  Wait, let me leave the room before you answer...

_________________________
Tim Maroney, duke!unc!tim

henry@utzoo.UUCP (Henry Spencer) (10/05/83)

A while ago, I exchanged mail with DMR about the question of whether
neglecting to declare the type of an unused return value was a sin.
The C book does say that awful things may happen if you get the type
wrong (explicitly or implicitly) and use the value, but it is silent
on the subject of return values that are merely discarded.  Dennis's
comments (as nearly as I remember them) were, roughly:  "There has been
no firm decision on this.  It would be a lot cleaner to require proper
declaration, but it would break many, many existing programs.  There is 
also an argument which says 'if you don't use it, why should you need
to declare it?'."  I believe the pragmatic issues involved made him
lean toward being permissive about this.  It does make life harder for
compiler writers, especially with structure returns.  About the only
thing you can do if your machine is especially recalcitrant is to have
the compiler quietly generate an extra little function for each "real"
function, with the extra function calling the real one and then properly
discarding the returned value.  The compiler then calls either the
real function or the extra, depending on whether the returned value is
used by the particular call or not.
-- 
				Henry Spencer @ U of Toronto Zoology
				{allegra,ihnp4,linus,decvax}!utzoo!henry

kendall@wjh12.UUCP (Sam Kendall) (10/05/83)

	The language should *not* guarantee that calling a function
	without having properly declared the type of the function's
	return value should work, because not all implementations can
	guarantee this.  If pointers and "int"s are different sizes...
	if you don't say "char *malloc()" or whatever you are
	*guaranteed* to lose.

	Guy Harris

You're absolutely right.  In my news item I was referring only to cases
in which the function is defined to return some type, possibly a
structure, but not declared (in another file) to do so, AND the return
value in that file is ignored; and to the language definition issues
raised by such examples.  But I didn't make this distinction clear in my
news item.

An example of what I mean:
	File "a.c":	struct a f()
			{
				...
			}

	File "b.c":	/* no declaration of f() */
			...
				f();	/* call to f, return value ignored */
			...
This example will fail on those machines which use a hidden first argument
to implement structure returning.  But this same lack of declaration is
common for functions which return something other than structures, functions
whose return values are often ignored anyway, such as strcpy().  My question,
then, is what should be guaranteed for such things.

Along these lines, I'd like to see a lint(1) option to warn about default
function and formal parameter declarations, even though they are part of the
language.

	Sam Kendall			{allegra,ihnp4}!wjh12!kendall
	Delft Consulting Corporation	  decvax!genrad!wjh12!kendall

decot@cwruecmp.UUCP (Dave Decot) (10/07/83)

The form

    (void) foo(x);

as a statement intended to "throw away" the value returned by foo(x) IS
correct, because it is a CAST operation on the expression

    foo(x);

which (apparently) has some non-void value.  The expression

    (void) foo(x);

still does have a "value" of sorts, but the "value" is of type void, whose
"conversion" rules are that any attempt to convert its values to any other
type (or to otherwise use void "values") is an error.  It is an therefore an
ERROR to ask for

    ((void) foo)(x);

because an expression of type void, i.e.

    ((void) foo)

cannot be where a function pointer (like foo) is required.

				Dave Decot
				..!decvax!cwruecmp!decot