[comp.lang.c] Invoking pointers to functions

dac@ukc.ac.uk (David Clear) (12/01/90)

Take a look at this:

main()
{
	int fred(), (*p)();

	p = fred;

	(*p)(10);		/* The right way */
	p(10);			/* This works too */
}

int
fred(x)
...

The (*p)(args) invocation is the K&R standard. p(args) also works on at
least 4 different Unix compilers.

Q: Is p(args) legal, portable C?
Q: Is p(args) preferential to (*p)(args) as it looks neater, compare:
	s->p(args)	(*s->p)(args)

Any thoughts? I've only ever used (*p)()... I only came across p() recently.

Dave.

volpe@camelback.crd.ge.com (Christopher R Volpe) (12/01/90)

In article <6379@harrier.ukc.ac.uk>, dac@ukc.ac.uk (David Clear) writes:
|>main()
|>{
|>	int fred(), (*p)();
|>
|>	p = fred;
|>
|>	(*p)(10);		/* The right way */
|>	p(10);			/* This works too */
|>}
|>Q: Is p(args) legal, portable C?

Yes

|>Q: Is p(args) preferential to (*p)(args) as it looks neater, compare:
|>	s->p(args)	(*s->p)(args)

I prefer (*p)(args) because the use matches the declaration and doesn't
cause me to go running around looking for the prototype declaration of
"function p". BTW, (*fred)() is just as legal as fred(). 
                          
==================
Chris Volpe
G.E. Corporate R&D
volpecr@crd.ge.com

henry@zoo.toronto.edu (Henry Spencer) (12/02/90)

In article <6379@harrier.ukc.ac.uk> dac@ukc.ac.uk (David Clear) writes:
>The (*p)(args) invocation is the K&R standard. p(args) also works on at
>least 4 different Unix compilers.
>
>Q: Is p(args) legal, portable C?

Once upon a time, it wasn't legal.  Now it is.  However, there are still
plenty of old compilers that may balk at it.  I'd say the code is clearer
and more portable if you make the `*' explicit.
-- 
"The average pointer, statistically,    |Henry Spencer at U of Toronto Zoology
points somewhere in X." -Hugh Redelmeier| henry@zoo.toronto.edu   utzoo!henry

jedelen@slate.mines.colorado.edu (Jeff Edelen @ Colorado School of Mines) (12/03/90)

In article <6379@harrier.ukc.ac.uk> dac@ukc.ac.uk (David Clear) writes:
>Take a look at this:
>
>main()
>{
>	int fred(), (*p)();
>
>	p = fred;
>
>	(*p)(10);		/* The right way */
>	p(10);			/* This works too */
>}
>
>int
>fred(x)
>...
>
>The (*p)(args) invocation is the K&R standard. p(args) also works on at
>least 4 different Unix compilers.
>
>Q: Is p(args) legal, portable C?
>Q: Is p(args) preferential to (*p)(args) as it looks neater, compare:
>	s->p(args)	(*s->p)(args)
>
>Any thoughts? I've only ever used (*p)()... I only came across p() recently.
>
>Dave.

It always seemed to me that p(args) is logically more consistent.  If you
call the function fred() normally with fred(args) and fred (no parens)
is a pointer to the function, then if p is a pointer to the same function,
just adding the parens should have the same effect.  As for what's legal...
anyone?

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (12/03/90)

In article <1990Dec1.232604.13487@zoo.toronto.edu> henry@zoo.toronto.edu (Henry Spencer) writes:
  [ calling a pointer to a function ]
> I'd say the code is clearer
> and more portable if you make the `*' explicit.

More portable, yes. However, it would be clearer if all function calls
were through pointers. The object referred to by a function name is
implemented as a constant pointer to the code of the function; the
language should accurately reflect this.

---Dan

martin@mwtech.UUCP (Martin Weitzel) (12/04/90)

In article <1990Dec02.204212.15465@slate.mines.colorado.edu> jedelen@slate.mines.colorado.edu (Jeff Edelen @ Colorado School of Mines) writes:
>In article <6379@harrier.ukc.ac.uk> dac@ukc.ac.uk (David Clear) writes:
>>Take a look at this:
>>
>>main()
>>{
>>	int fred(), (*p)();
>>
>>	p = fred;
>>
>>	(*p)(10);		/* The right way */
>>	p(10);			/* This works too */
>>}
>>
>>int
>>fred(x)
>>...
>>
>>The (*p)(args) invocation is the K&R standard. p(args) also works on at
>>least 4 different Unix compilers.
>>
>>Q: Is p(args) legal, portable C?
>>Q: Is p(args) preferential to (*p)(args) as it looks neater, compare:
>>	s->p(args)	(*s->p)(args)
>>
>>Any thoughts? I've only ever used (*p)()... I only came across p() recently.
>>
>>Dave.
>
>It always seemed to me that p(args) is logically more consistent.  If you
>call the function fred() normally with fred(args) and fred (no parens)
>is a pointer to the function, then if p is a pointer to the same function,
>just adding the parens should have the same effect.  As for what's legal...
>anyone?

You have discovered one of the shortcuts that make C hard to learn.

I like the "old" style that require the derefencing `*' when you call
a function through a pointer (which you consider as `logically less
consistent'). I see it just from the other side: It is logically less
consistent that `fred (no parens)' is a pointer to the function.
It would be more consistent to require `&fred' for the latter. (In fact
writing `&fred' is allowed in ANSI-C).

An explanation why I would prefer `(*p)()' over `p()' is lengthy. Hit the
n-key now, if you aren't interested in details.

Still here? OK, let's consider the following declaration, which looks
rather complicated, but is simple to read if you know the rules:

	int *(*(*foo)[10])(int, int);
	                            This declares `foo' as
	        ^------------------ pointer to an
	             ^^^^---------- array of 10
	      ^-------------------- pointers to
	                   ^^^^^^^^ function taking two int args returning a
	    ^---------------------- pointer to an
	^^^------------------------ int.

USING foo is *absolutely symmetric* to this declaration:

1)	         foo		 => pointer to an
	                            array of 10
	                            pointers to
	                            function taking two int args returning a
				    pointer to an
	                            int
2)	        *foo		 => array of 10
	                            pointers to
	                            function taking two int args returning a
	                            pointer to an
	                            int
3)	       (*foo)[5]	 => pointer to
	                            function taking two int args returning a
	                            pointer to an
	                            int.
4)	      *(*foo)[5]	 => function taking two int args returning a
	                            pointer to an
	                            int.
5)	     (*(*foo)[5])(1,2)	 => pointer to an
	                            int
6)	    *(*(*foo)[5])(1,2)   => int

Watch how nicely the resulting type matches exactly the declaration,
with the DECLARATORS you have allready applied as OPERATORS skipped over.

Now let's consider ANSI-C. It specifies that a function call should be
applied to an expression with type `pointer to function'. This would
require leaving out step 4 (dereferencing the function pointer to gain
access to the function) and break the nice scheme.

6a)	   *((*foo)[5])(1,2)    => int

Now you can leave out one level of parenthesis, resulting in

6b)	   *(*foo)[5](1,2)      => int

ANSI-C legalizes existing practice, i.e. what I originally showed as
step 6), by converting an expression of type `function' automatically
to type `pointer to function' (except in the context of the `&' and
`sizeof' operators). 

It is hard to explain to someone who just learns C why you need an `*'
in the declaration which you don't need when you use the object, so I
clearly prefer applying the dereferencing operator to a function
pointer before making the call.

And the way how ANSI-C now manages to make the right thing out of 6)

	    *(*(*foo)[5])(1,2)
	       ^^^^^^^^^^------------ pointer to function
	      ^^^^^^^^^^------------- function
	                              since not in context of `&' or `sizeof'
	                              automatically converted to
	     ^^^^^^^^^^^^------------ pointer to function
	     ^^^^^^^^^^^^^^^^^------- call to this function via pointer
	    ^------------------------ dereferencing the result

seems rather complicated, and much more difficult to explain! You could
also write
	    *(**(*foo)[5])(1,2)
or
	    *(***(*foo)[5])(1,2)
etc.
The source for all this trouble is burried in the original design of C. If
K&R had required that with `bar() { ..... }' you must use `&bar' whenever
you want the adress of the function, there would have been no need for any
exception from the general rule.

Furthermore, there would have been clear distinction between `function
pointers' and `functions': You can think of the latter as the part of
the code segment which holds the machine instructions of that particular
function. A pointer to a function is the adress of the first instruction.
What you can do with a function (in the whole) is take the adress or
execute it. This is quite similar as with struct-s, where the name of
the object denotes the whole thing, to which you can only apply a few
operators (`&' for taking the adress and `.' for selecting a component).

Maybe Doug Gwyn knows the motivation why X3J11 decided that functions are
called through pointers with the special rule that the type `function' in
part of an expression automatically becomes `pointer to function'. IMHO
it would have been more logical to require that calls are made to
`functions', not to `pointers to function'. Existing practice (name of a
function means adress of this funtion) could have been allowed with the
exception that the type `function' outside the context of a call operator
is automatically converted to `pointer to function').
-- 
Martin Weitzel, email: martin@mwtech.UUCP, voice: 49-(0)6151-6 56 83

gwyn@smoke.brl.mil (Doug Gwyn) (12/05/90)

In article <989@mwtech.UUCP> martin@mwtech.UUCP (Martin Weitzel) writes:
>Maybe Doug Gwyn knows the motivation why X3J11 decided that functions are
>called through pointers with the special rule that the type `function' in
>part of an expression automatically becomes `pointer to function'.

You don't have to ask me; the Rationale document explains the reasoning.

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (12/05/90)

In article <989@mwtech.UUCP> martin@mwtech.UUCP (Martin Weitzel) writes:
  [ usage should be absolutely symmetric to declaration ]

Agreed.

  [ in ANSI C, if foo points to a function, you can call foo(x) ]
  [ this breaks symmetry ]

Agreed.

  [ therefore only (*foo)(x) should be allowed ]

Well, that's one solution, but this one is more natural: The name of a
function has type ``pointer to function.'' So after int foo() { ... },
the object ``foo'' refers to has type pointer-to-function-returning-int.
And all functions are called through function pointers. Symmetry is
preserved.

Why is this more natural? Because it's how the machine does it. In the
absence of any other criteria, the realistic solution is the natural
one.

---Dan

karl@ima.isc.com (Karl Heuer) (12/09/90)

Martin Weitzel said pretty much what I wanted to, so I'll just describe the
situation from a different angle.

Function-call semantics in ANSI C may be described by either of two models
(here f is a function and pf is a pointer-to-function):

(a) Functions decay into pointers in any rvalue context, and there are no
interesting lvalue contexts for them, so they are practically a nonexistent
type.  The interesting operations are
	(): pointer -> result-type
	&: function -> pointer, but this is redundant
	*: pointer -> function, but it will immediately decay back
In the expression `pf = f' the two sides have matching type because of the
decay of `f' (unlike `f = pf' which would be an attempt to use `f' in an
illegal lvalue context).  `pf()' has the obvious meaning, and `f()' also works
since `f' decays to the same type as `pf' before the `()' operator applies.

(b) Functions and pointers are logically distinct types.  The interesting
operations are
	(): function -> result-type
	&: function -> pointer
	*: pointer -> function
The expressions `pf = &f', `f()', and `(*pf)()' have the obvious meanings.
As a special kludge, uttering the name `f' in a context other than as an
operand of `&' or `()' causes an implicit conversion to pointer (as if by
`&'), and uttering the name `pf' in a call context causes an implicit
conversion to function (as if by `*').

Since the two models produce identical results, either can be said to be
correct.  (The fact that the ANS describes the world in terms of model (a) is
not important.)  I happen to think model (b) is cleaner, so that's the one I
use.

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

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (12/10/90)

In article <1990Dec08.214057.6280@dirtydog.ima.isc.com> karl@ima.isc.com (Karl Heuer) writes:
> Function-call semantics in ANSI C may be described by either of two models
> (here f is a function and pf is a pointer-to-function):

What's common to your two models is that both involve kludges,
expressions decaying from one type into another, etc. Let me describe a
third model that would work perfectly well for C, doesn't require any
hacks in normal situations, and is quite intuitive.

(c) Functions and pointers are logically distinct types. Note that the
name of a function refers to type pointer-to-function, *not* function.
The interesting operations are
	(): pointer -> result-type
	&: function -> pointer, but this is generally redundant (you
	   almost never use functions directly in the first place)
	*: pointer -> function, but this is pointless (you only call
	   functions through pointers, so why use * unless you want to
	   see the actual instructions inside the function?)
As special (deprecated) kludges: Any function in a () context is
converted into the pointer. Any pointer-to-function in a & context is
left alone.

Surely you admit that this is just as clean as your model (b). It has
the advantage of being more realistic---at the machine level you call
functions by address, not by value. And it's a lot closer to common
practice before ANSI. So ANSI could have settled on the above model.

---Dan

karl@ima.isc.com (Karl Heuer) (12/12/90)

In article <2284:Dec1001:01:4690@kramden.acf.nyu.edu> brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes:
>What's common to your two models is that both involve kludges,

I don't think that's avoidable, given the semantics.

>(c) Functions and pointers are logically distinct types. Note that the
>name of a function refers to type pointer-to-function, *not* function.
>...Any pointer-to-function in a & context is left alone.

Doesn't work.  You need to have `&f' yield pointer-to-function, while `&pf'
yields pointer-to-pointer-to-function.  Thus, `f' and `pf' must have different
types in an lvalue context.

>It has the advantage of being more realistic---at the machine level you call
>functions by address, not by value.

If we were to continue in that vein, I'd also have to say that the left
operand of the assignment operator should logically be a pointer instead of an
lvalue.  That way lies BLISS.

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