Invoking pointers to functions (C sytle)

Martin Weitzel martin at mwtech.UUCP
Tue Dec 4 09:18:43 AEST 1990


In article <1990Dec02.204212.15465 at slate.mines.colorado.edu> jedelen at slate.mines.colorado.edu (Jeff Edelen @ Colorado School of Mines) writes:
>In article <6379 at harrier.ukc.ac.uk> dac at 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 at mwtech.UUCP, voice: 49-(0)6151-6 56 83



More information about the Comp.lang.c mailing list