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