Path: utzoo!utgpu!news-server.csri.toronto.edu!rutgers!att!tut.cis.ohio-state.edu!zaphod.mps.ohio-state.edu!wuarchive!uunet!mcsun!unido!mikros!mwtech!martin From: martin@mwtech.UUCP (Martin Weitzel) Newsgroups: comp.lang.c Subject: Re: Invoking pointers to functions (C sytle) Message-ID: <989@mwtech.UUCP> Date: 3 Dec 90 22:18:43 GMT References: <6379@harrier.ukc.ac.uk> <1990Dec02.204212.15465@slate.mines.colorado.edu> Reply-To: martin@mwtech.UUCP (Martin Weitzel) Organization: MIKROS Systemware, Darmstadt/W-Germany Lines: 148 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 Brought to you by Super Global Mega Corp .com