[comp.lang.c] Another portable code question

g-rh@cca.CCA.COM (Richard Harter) (06/13/87)

	Here is one that has me puzzled.  We have a C program which
runs on a large variety of UNIX boxes (system V and bsd), VMS, and PRIMOS.
One of the files has code that looks like this:

extern int (*command_table[])();
.....
command_processor() {
....
int (*command)();
....
command = command_table[n];
....
(*command)(args);
....
}

	The declaration for command_table declares it as an array of
pointers to functions returning int.  (Confirmed by cdecl)  The declar-
ation for command declares it as a pointer to a function returning int.
We pick off the appropriate command and execute it.  Now, on a wide
variety of UNIX systems the line "(*command)(args);" may be written
as "(command)(args)".  We have observed:

(a)	On most UNIX machines (VAX BSD and Sun in particular) either
form is acceptable and will compile and execute correctly.  Moreover
lint will accept either form without complaint.  (VMS C also takes
either form without complaint or error.)

(b)	One vendor's compiler (I won't name it because I don't have the
log sheets at hand) gives a compilation warning for (*command)().
(This is a UNIX box.)

(c)	The PRIMOS C compiler rejects (command)(args) and insists
upon (*command)(args)

First Question:  Is the above code correct and legal C, or are we 
overlooking something subtle?

Second Question:  Why do both alternatives pass lint and execute
correctly on most machines?

Third Question:  Are we correct in assuming that the compiler for 
vendor X is broken?

Fourth Question:  We are strongly committed to portable code and would
like the code to be the same, in so far as possible, to be the same for
all operating systems that we deal with.  we have good reasons for using
a jump table.  What is the best way to code this with full portability?
-- 

Richard Harter, SMDS Inc. [Disclaimers not permitted by company policy.]

stevesu@copper.UUCP (06/14/87)

In article <16673@cca.CCA.COM>, g-rh@cca.CCA.COM (Richard Harter) writes:
> ...on a wide variety of UNIX systems the line "(*command)(args);"
> may be written as "(command)(args)".  We have observed:
> 
> (a)	On most UNIX machines (VAX BSD and Sun in particular) either
> form is acceptable and will compile and execute correctly.
> 
> (b)	One vendor's compiler gives a compilation warning for (*command)().
> 
> (c)	The PRIMOS C compiler rejects (command)(args) and insists
> upon (*command)(args)

I believe that the compiler mentioned on observation (b) is
incorrect.  This may be a perpetuation of the (possibly
misguided) trend of issuing warnings for &array.

I have always thought about it this way:  if "command" is a
pointer to a function, then I have to slap a "*" in front of it
before I have a real function that I can stick an argument list
on and call.

Many compilers seem to make the "*" optional, reasoning that
the only possible use for a function pointer, especially in
a function call context, is to indirect on it and call it.
I wish they wouldn't, since this only leads to confusion and
misunderstanding, as you've discovered.  (Throwing well-defined
rules out the window and applying weird heuristics in an attempt
to do what the programmer meant and not what he said violates
what I feel to be one of the fundamental tenets of the Unix
philosophy.  One of the reasons that programming on VMS is such a
screaming nightmare is that there are very few hard-and-fast
rules you can apply in attempting to predict the system's
behavior, since so many undocumented special cases have been
inserted so as to "do the right thing.")

So, to answer your question, (*command)(args) is correct and
should be maximally (but, sadly, not universally) portable.

                                           Steve Summit
                                           stevesu@copper.tek.com

guy@sun.UUCP (06/15/87)

> > (b)	One vendor's compiler gives a compilation warning for (*command)().
> > 
> > (c)	The PRIMOS C compiler rejects (command)(args) and insists
> > upon (*command)(args)
>
> I believe that the compiler mentioned on observation (b) is
> incorrect.

The compiler mentioned on observation (b) is incorrect.  According to
K&R:

	7.1 Primary expressions

	...

	   A function call is a primary expression followed by
	parentheses containing a possibly empty, comma-separated list
	of expressions which constitute the actual arguments to the
	function.  The primary expression must be of type "function
	returning..." ...

And, if "command" is of type "pointer to function returning...",
"*command" is clearly of type "function returning.

ANSI C is a bit different:

	3.3.2.2 Function calls

	Constraints

	   The expression that denotes the function called shall have
	type ``pointer to function''.

Since an expression of type "function" gets converted to an
expression of type "pointer to function" that points to that
function, in most cases - see

	3.3.0.1 Lvalues and function designators

	...

	   A "function designator" is an expression that has function
	type.  If a function designator appears in a context where
	such an operand is not permitted, it is converted to a
	pointer as described in \(sc 3.2.2.1.  The result is not a
	function designator

and

	3.2.2.1 Arrays, functions, and pointers

	...

	   Except when used as an operand where a function designator
	is permitted, an identifier declared as ``function returning
	"type"'' is converted to an expression that has type
	``pointer to function returning "type".''

- either (*command)(args) or command(args) is permitted by ANSI C.

So it seems that K&R permits (b) but not (c), while ANSI C permits
both of them.  I'm not sure I like this either, but that's life.

> This may be a perpetuation of the (possibly
> misguided) trend of issuing warnings for &array.

Actually, ANSI C changes the *meaning* of "&array", so that it is an
expression of type "pointer to array" rather than an expression of
type "pointer to what the array is made out of".  As such, this trend
will come to an end once the ANSI C standard leaves draft status.
(Fortunately, this is one of the easiest pieces of ANSI C to
implement in PCC - all you have to do is delete some code, namely the
code in "mip/cgram.y" that tests whether the "&" operator is being
applied to something of type "array" or "function", issues a warning,
and eliminates the "&" operator.  It's always nice to improve
something without having to add code....)
-- 
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com (or guy@sun.arpa)

chris@mimsy.UUCP (06/15/87)

In article <16673@cca.CCA.COM>, g-rh@cca.CCA.COM (Richard Harter) writes:
>int (*command)();
>....
>(*command)(args);

vs

>"(command)(args)"

>One vendor's compiler ... gives a compilation warning for (*command)().
>... The PRIMOS C compiler rejects (command)(args) and insists
>upon (*command)(args).

The PRIMOS C compiler is within its rights; K&R have said to use
the first form.  The dpANS recommends the second form.  When the
draft standard is adopted, and assuming that its recommendation
still stands, the PRIMOS compiler will have to be changed.  The
compiler that gives a warning for (*command)(args) is making useless
noise.

Incidentally, PCC accepts all sorts of strange things:

	x()
	{
		int (*f)(), g();

		f = g;
		(*f)(1);
		f(1);
		(***f)(1);
		g(1);
		(*g)(1);
		(**g)(1);
	}

If you look at it properly, this is not so strange after all:
Values of type `function' are changed to values of type `pointer
to function' in most contexts.  Why not change them thus even in
function call contexts, and then allow

	pointer-to-function '(' argument-list ')' ';'

and accept either form?
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
Domain:	chris@mimsy.umd.edu	Path:	seismo!mimsy!chris

levy@ttrdc.UUCP (06/15/87)

In article <16673@cca.CCA.COM>, g-rh@cca.CCA.COM (Richard Harter) writes:
< extern int (*command_table[])();
< .....
< command_processor() {
< ....
< int (*command)();
< ....
< command = command_table[n];
< ....
< (*command)(args);
< ....
< }
< 
< One vendor's compiler (I won't name it because I don't have the
< log sheets at hand) gives a compilation warning for (*command)().
< (This is a UNIX box.)

I'm curious what the warning is.  Could the compiler be griping because
you do nothing with the return value from (*command)(args) ?  (Lint should
complain about this too.)
-- 
|------------dan levy------------|  Path: ..!{akgua,homxb,ihnp4,ltuxa,mvuxa,
|         an engihacker @        |		vax135}!ttrdc!ttrda!levy
|    at&t data systems division  |  Disclaimer:  try datclaimer.
|--------skokie, illinois--------|

g-rh@cca.CCA.COM (Richard Harter) (06/16/87)

In article <1763@ttrdc.UUCP> levy@ttrdc.UUCP (Daniel R. Levy) writes:
>< ....
>< (*command)(args);
>< ....
>< One vendor's compiler (I won't name it because I don't have the
>< log sheets at hand) gives a compilation warning for (*command)().
>< (This is a UNIX box.)
>

	As I recall, the warning was to the effect that the * was not
needed.  As to lint.  I trimmed the code -- the actual statement was
of the for 'value = (*command)(args))'.  However lint should not complain
and does not if a function is explicitly or implicitly declared int and
you are consistent either in using or not using the return value.  The
cast to void kludge postdates lint by a fair margin.  I won't say that
there are no version extant of lint that will complain about consistently
ignoring an int return, but I have run on a fair number of machines,
and I haven't seen it.

We don't use void casts or enums as a matter of policy.  We aren't
interested in using pretty little frills that may or may not be
universally available.  Nor are we interested in challenging people's
compilers and discovering the obscure ways they are broken.  Our
coding precepts are very simple -- if there is any prospect that it
might not work on any of the machines that we support, don't use it.
Special cases and non-portability code are too expensive to bother
with.

-- 

Richard Harter, SMDS Inc. [Disclaimers not permitted by company policy.]

daveb@geac.UUCP (Dave Brown) (06/16/87)

In article <7048@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>If you look at it properly, this is not so strange after all:
>Values of type `function' are changed to values of type `pointer
>to function' in most contexts.  Why not change them thus even in
>function call contexts, and then allow
>	pointer-to-function '(' argument-list ')' ';'
>and accept either form?

    Probably for the same reason that caused so much discussion over
automatic changing of array-name to pointer-to-array  -vs- taking
the address of an array-name:  the more the compiler does for you,
the more it has to guarantee that what it did will never cause 
future problems.  
    Friendlyness -> Complexity -> Defaults -> PL/1 
    My solution? Promote all the niceties to a language
called c+, c+-, c[1] or whatever and remove some of the warts on 
regular c.  Then call *that* language c--.

 --dave (then *REAL PROGRAMMERS* would use c-- instead of fortran) brown

jfh@killer.UUCP (John Haugh) (06/19/87)

In article <16769@cca.CCA.COM>, g-rh@cca.CCA.COM (Richard Harter) writes:
> In article <1763@ttrdc.UUCP> levy@ttrdc.UUCP (Daniel R. Levy) writes:
> >< ....
> >< (*command)(args);
> >< ....
> 
> 	As I recall, the warning was to the effect that the * was not
> needed.  As to lint.  I trimmed the code -- the actual statement was
> of the for 'value = (*command)(args))'...

Sorry if this has been said this week, I said it a few weeks back.
Function declarations are wierd in C.  The only language that seems
to get pointers reasonably normal (or non-wierd) is BLISS.  But then I
hate all of those silly little dots.  [ This is just inews fodder ]

Heres the short form on function declarations.  Write Dennis if you
want more, or better still go buy the book.

int	func();			/* function returning integer */
int	*func();		/* function returning pointer to integer */
int	(*func)();		/* pointer to function returning integer */
int	*(*func)();		/* pointer to function returning * to int */

How do you use these guys to get an integer after you call them?

	i = func ();		/* returns integer, use that */
	i = *(func ());		/* returns pointer, dereference it */
	i = (func) ();		/* use pointer, take return value */
	i = *((func) ());	/* use pointer, dereference return value */
 
There it is in a nutshell, you really don't need the * unless the function
is declared int (**func)();, then you have pointer to pointer to a function
returning an integer.

> Richard Harter, SMDS Inc. [Disclaimers not permitted by company policy.]

- John.

Disclaimer:
	No disclaimer.  Whatcha gonna do, sue me?

john@riddle.UUCP (Jonathan Leffler) (06/22/87)

>In article <16673@cca.CCA.COM>, g-rh@cca.CCA.COM (Richard Harter) writes:
>> ...on a wide variety of UNIX systems the line "(*command)(args);"
>> may be written as "(command)(args)".

For what it is worth, on the ICL Perq, the syntax
	command(args)
was interpreted as
	(*command)(args)
which makes some sort of sense (all function names can be thought of
as pointers to functions).  We only learnt that this was unusual when
we ported to other compilers (lots of 'em), and they only accepted (*f)().

------
My views are mine.
Jonathan Leffler

peter@sugar.UUCP (Peter DaSilva) (06/26/87)

> There it is in a nutshell, you really don't need the * unless the function
> is declared int (**func)();, then you have pointer to pointer to a function
> returning an integer.

According to K and R (1) You do need the *, and (2) (func)() should be
identical to func(). I'm not sure I approve of compilers taking it upon
themselves to turn func() into (*func)() without telling me, and I certainly
won't assume that it will work in any compiler I might come across. The
meaning of pointers to functions should be well defined. Lately the meanings
of all sorts of things has become problematical.

While I'm here, let me bitch about structure passing. I really don't think
there's any reason for passing anything larger than a double on the stack.
If you want to have a routine to copy it into local storage, you can always
allocate another copy on the stack in the routine and memcpy() it. It won't
be significantly slower. On the other hand I've been bitten more than once
by leaving out an ampersand and having 10,000 bytes pushed on an 8,000 byte
stack. UNIX => C but C !=> UNIX.

francus@cheshire.columbia.edu (Yoseff Francus) (06/30/87)

In article <225@sugar.UUCP> peter@sugar.UUCP (Peter DaSilva) writes:
>> There it is in a nutshell, you really don't need the * unless the function
>> is declared int (**func)();, then you have pointer to pointer to a function
>> returning an integer.
>
>According to K and R (1) You do need the *, and (2) (func)() should be
>identical to func(). I'm not sure I approve of compilers taking it upon
>themselves to turn func() into (*func)() without telling me, and I certainly
>won't assume that it will work in any compiler I might come across. The
>meaning of pointers to functions should be well defined. Lately the meanings
>of all sorts of things has become problematical.
>
>While I'm here, let me bitch about structure passing. I really don't think
>there's any reason for passing anything larger than a double on the stack.
>If you want to have a routine to copy it into local storage, you can always
>allocate another copy on the stack in the routine and memcpy() it. It won't
>be significantly slower. On the other hand I've been bitten more than once
>by leaving out an ampersand and having 10,000 bytes pushed on an 8,000 byte
>stack. UNIX => C but C !=> UNIX.


True ususally you want to pass the address of a strucuture.  But what if
you change the values of the structure in the function, and DON'T want
to know about the changes after returning from the function????

Yoseff



******************************************************************
yf
In Xanadu did Kubla Khan a stately pleasure dome decree
But only if the NFL to a franchise would agree.

ARPA: francus@cs.columbia.edu
UUCP: seismo!columbia!francus

mlinar@poisson.usc.edu (Mitch Mlinar) (06/30/87)

In article <4761@columbia.UUCP> francus@cheshire.columbia.edu.UUCP (Yoseff Francus) writes:
>In article <225@sugar.UUCP> peter@sugar.UUCP (Peter DaSilva) writes:
>>> There it is in a nutshell, you really don't need the * unless the function
>>> is declared int (**func)();, then you have pointer to pointer to a function
>>> returning an integer.
>>
>
>True ususally you want to pass the address of a strucuture.  But what if
>you change the values of the structure in the function, and DON'T want
>to know about the changes after returning from the function????
>
>Yoseff
>

Maybe I am not too familiar with the BIG frame architectures, but I am
somewhat familiar with items of the Sun/uVaxII genre on down.  The issue
actually boils down to efficiency of structures and the simple fact is:

Operations on static variables are ALWAYS faster than operations on auto
variables.

Now, this could just be a weakness in the uP instruction set or in the C
compilers themselves, but I have yet to see the reverse.  If you insist
upon doing big structs as "auto" (stack) variables, then there is probably
not much difference in speed - both passing a structure on the stack and
copying to a local "auto" are slow.  However, using stack operations on
these machines is MUCH slower than passing a pointer and copying to a
local static area.  Secondly, MANY C compilers still do not support pushing
anything other than simple arguments on the stack.  Pushing whole structures
is NOT portable in my book.

As it turns out, almost ALL efficient C compilers handle static memory
allocation much better than stack, too.  So, you are not saving any REAL
memory, although swap decreases.  (Use of static is REQUIRED in our Sun 3.0
compilers here at USC, ANY auto variable over 16k-bytes in size gets lunched
on the second recursive entry.  It took me 2 weeks and head bashing with
the also buggie dbxtool to figure this one out.  By breaking things up to
<16k-bytes and using static, the code zooms!)

-Mitch

mat@mtx5a.COM (m.terribile) (07/06/87)

> >>> There it is in a nutshell, you really don't need the * unless the function
> >>> is declared int (**func)();, then you have pointer to pointer to a fun
> >>> tion returning an integer.
> >
> >True ususally you want to pass the address of a strucuture.  But what if
> >you change the values of the structure in the function, and DON'T want
> >to know about the changes after returning from the function????
> 
> Maybe I am not too familiar with the BIG frame architectures, but I am
> somewhat familiar with items of the Sun/uVaxII genre on down.  The issue
> actually boils down to efficiency of structures and the simple fact is:
> 
> Operations on static variables are ALWAYS faster than operations on auto
> variables.

No it does not, and no they are not.

It does not boil down to that because the question of *what* you are pssing,
and with what semantics, will not go away just because you want it to.

And no, static references are not always cheaper.  On the 68000, a reference
to a something at a fixed address requires 2 to 4 bytes more memory and takes
2 to 4 extra clock cycles than a reference relative to an address register.

> ...  If you insist upon doing big structs as "auto" (stack) variables,
> then there is probably not much difference in speed - both passing a
> structure on the stack and copying to a local "auto" are slow.

You do what you need to to get the semantics you need.  If you don't need
it, don't do it.

> ...  Secondly, MANY C compilers still do not support pushing anything other
> than simple arguments on the stack.  Pushing whole structures is NOT
> portable in my book.

Refusing to use an enhancement that became available years ago and has become
as near to standard as anything else in the language is a disservice to
yourself and to the people who worked to create the enhancement.

> As it turns out, almost ALL efficient C compilers handle static memory
> allocation much better than stack, too.  So, you are not saving any REAL
> memory, although swap decreases.  (Use of static is REQUIRED in our Sun 3.0
> compilers here at USC, ANY auto variable over 16k-bytes in size gets lunched
> on the second recursive entry.

*THIS* is a nonstandard compiler bug.

> It took me 2 weeks and head bashing with the also buggie dbxtool to figure
> this one out.  By breaking things up to <16k-bytes and using static, the
> code zooms!)

Arguing from bugs through bugs what can you expect except misfeatures?
-- 

	from Mole End			Mark Terribile
		(scrape .. dig )	mtx5b!mat
					(Please mail to mtx5b!mat, NOT mtx5a!
						mat, or to mtx5a!mtx5b!mat)
					(mtx5b!mole-end!mat will also reach me)
    ,..      .,,       ,,,   ..,***_*.