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