Casting pointers to varargs functions

G

Gene

Hello all. Is the initializing assignment below ANSI standard-
conforming?

Is there a way to have the prefix parameters to ... type checked in
such an assignment? E.g. in this case int x in the typedef matched
with int a of foo?

Appreciate the help.

------------

#include <stdio.h>

int foo(int a, int b)
{
return a + b;
}

typedef int (*FUNCTION)(int x, ...);

int main(void)
{
FUNCTION p = (FUNCTION)foo;
printf("%d\n", (*p)(1, 2));
return 0;
}
 
E

Eric Sosman

Gene said:
Hello all. Is the initializing assignment below ANSI standard-
conforming?

Yes, but ...
Is there a way to have the prefix parameters to ... type checked in
such an assignment? E.g. in this case int x in the typedef matched
with int a of foo?

Yes, the fixed parameters behave like normal prototyped
parameters, but ...
Appreciate the help.

------------

#include <stdio.h>

int foo(int a, int b)
{
return a + b;
}

typedef int (*FUNCTION)(int x, ...);

int main(void)
{
FUNCTION p = (FUNCTION)foo;
printf("%d\n", (*p)(1, 2));

... but right here your program goes off the rails. The
function pointer type ("pointer to function returning int,
taking an int and variable arguments") does not match the type
of the called function ("function returning int, taking two
int arguments"). When you call a function via a pointer that
doesn't match the function's actual type, the behavior is
undefined.

It's not a matter of caprice. Different machines use
different mechanisms for subroutine linkage, and the "normal"
argument passing mechanism may be unsuitable for variable
argument lists. For example, imagine a machine with separate
CPU registers for integer and floating-point values; a call
to `foo(int x, double y, int z)' might use R0 and R1 for x
and z, and F0 for y. But this convention could be clumsy for
`bar(int q, ...)' even if the remaining arguments in some call
turn out to be a double and an int. An implementation might
invoke bar by passing q in R0 and a pointer to a memory-resident
aggregation of the remaining arguments in R1. If the caller
uses one convention and the callee uses something else, then
"What we have here is a failure to communicate."
 
G

Gene

Yes, but ...


Yes, the fixed parameters behave like normal prototyped
parameters, but ...







... but right here your program goes off the rails. The
function pointer type ("pointer to function returning int,
taking an int and variable arguments") does not match the type
of the called function ("function returning int, taking two
int arguments"). When you call a function via a pointer that
doesn't match the function's actual type, the behavior is
undefined.

It's not a matter of caprice. Different machines use
different mechanisms for subroutine linkage, and the "normal"
argument passing mechanism may be unsuitable for variable
argument lists. For example, imagine a machine with separate
CPU registers for integer and floating-point values; a call
to `foo(int x, double y, int z)' might use R0 and R1 for x
and z, and F0 for y. But this convention could be clumsy for
`bar(int q, ...)' even if the remaining arguments in some call
turn out to be a double and an int. An implementation might
invoke bar by passing q in R0 and a pointer to a memory-resident
aggregation of the remaining arguments in R1. If the caller
uses one convention and the callee uses something else, then
"What we have here is a failure to communicate."

Thanks Eric. That's just what I need and sort of what I expected.
What if I say instead

typedef int (*FUNCTION)();

? I.e. use no prototype at all for the parameters a la K&R. Is this
now reliable?

(Interesting that gcc doesn't complain about the first first form even
with -Wall --ansi --pedantic.)
 
I

Ian Collins

*Please* don't quote signatures...
Thanks Eric. That's just what I need and sort of what I expected.
What if I say instead

typedef int (*FUNCTION)();

? I.e. use no prototype at all for the parameters a la K&R. Is this
now reliable?
If what context? IIRC if you use a K&R style prototype, any parameters
passed will be promoted to int, which might not be what you want.
(Interesting that gcc doesn't complain about the first first form even
with -Wall --ansi --pedantic.)
Not at all, you told it not to complain with that horrible cast!
 
B

Ben Pfaff

Ian Collins said:
If what context? IIRC if you use a K&R style prototype, any parameters
passed will be promoted to int, which might not be what you want.

Point of terminology: "K&R style prototype" is a contradiction in
terms. You can have a declaration that is not a prototype, and
I'd even allow that you can have a K&R-style function definition,
but you can't have a K&R style prototype.
 
I

Ian Collins

Ben said:
Point of terminology: "K&R style prototype" is a contradiction in
terms. You can have a declaration that is not a prototype, and
I'd even allow that you can have a K&R-style function definition,
but you can't have a K&R style prototype.

Fair point.
 
E

Eric Sosman

Gene said:
[...]
#include <stdio.h>
int foo(int a, int b)
{
return a + b;
}
typedef int (*FUNCTION)(int x, ...);
int main(void)
{
FUNCTION p = (FUNCTION)foo;
printf("%d\n", (*p)(1, 2));

Thanks Eric. That's just what I need and sort of what I expected.
What if I say instead

typedef int (*FUNCTION)();

? I.e. use no prototype at all for the parameters a la K&R. Is this
now reliable?

Yes, but the meaning may not be exactly what you expect.
It now says that a variable of type FUNCTION is a "pointer to
a function returning int, taking a fixed number of arguments,
but the quantity and types of those arguments aren't specified."
This matches the actual type of your foo(), but you could not
use a FUNCTION variable to call printf(), say, because printf()
takes a variable number of arguments. (There *is* a difference
between "variable number" and "unspecified fixed number.")

More subtly, you couldn't use a FUNCTION variable to call
a function with any "promotable" parameters. In the absence of
a prototype (which FUNCTION doesn't provide), a `float' argument
is promoted to `double'. If the called function actually expects
to receive a `float', it is likely to get confused by the wrong-
type argument. Similar problems can affect `char' and `short' and
so on, which will promote to `int' or `unsigned int' depending on
circumstances.
(Interesting that gcc doesn't complain about the first first form even
with -Wall --ansi --pedantic.)

Blame it on the cast operator. By writing it, you told the
compiler (more or less) that you knew what you were doing and
voluntarily accepted the risks thereof. The compiler shrugged
its cybernetic shoulders, washed its digital hands, and obligingly
handed you all the rope you wanted ... ;-)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,145
Latest member
web3PRAgeency
Top