function pointers as function parameters

  • Thread starter Marlene Stebbins
  • Start date
M

Marlene Stebbins

I am experimenting with function pointers. Unfortunately, my C book has
nothing on function pointers as function parameters. I want to pass a
pointer to ff() to f() with the result that f() prints the return value
of ff(). The code below seems to work, but I would appreciate your
comments. Have I got it right? Does the function name "decay" to a pointer?


#include <stdio.h>
/* declares a function which takes an argument
that is a pointer to a function returning an int */
void f(int (*fptr)());
/* function returning an int */
int ff(void);

int main(void)
{
f(ff); /* pass the address of ff to f */

return 0;
}

void f(int (*fptr)())
{
int a;
a = (*fptr)(); /* deref the func pointer */
printf("%d\n", a);
return;
}

int ff(void)
{
return 2345;
}
 
E

Eric Sosman

Marlene said:
I am experimenting with function pointers. Unfortunately, my C book has
nothing on function pointers as function parameters. I want to pass a
pointer to ff() to f() with the result that f() prints the return value
of ff(). The code below seems to work, but I would appreciate your
comments. Have I got it right? Does the function name "decay" to a pointer?

It might be simpler to say that the function name "is"
a pointer to the function, since there is no context in
which it does anything other than "decay."

Your code is correct (as far as I can see), but there
are a few opportunities for stylistic improvement:
#include <stdio.h>
/* declares a function which takes an argument
that is a pointer to a function returning an int */
void f(int (*fptr)());

You know more about the pointed-to function than the
type of its returned value: specifically, you know that
it takes no arguments. (How do you know this? Because
you supply no arguments when you call it.) On the general
principle that it's best to tell the compiler everything
you know, write the declaration as

void f(int (*fptr)(void));

This way, the compiler will protest if you inadvertently
try to call the pointed-to function with arguments. If
you do in fact want to pass arguments, say so:

void f2(int (*fptr)(double, int, const char*));
/* function returning an int */
int ff(void);

int main(void)
{
f(ff); /* pass the address of ff to f */

Here's where ff "decays."
return 0;
}

void f(int (*fptr)())

If you've provided a prototype as suggested above,
you should also do so here.
{
int a;
a = (*fptr)(); /* deref the func pointer */

This can also be written `a = fptr();' without the
parentheses and the asterisk. Some people prefer to write
the call as you've done it, saying that it draws attention
to the fact that a function pointer variable (rather than
a function identifier) is being used. I personally don't
buy that argument, noting that

(*printf)("Hello, world!\n");

is equally legitimate and (IMHO) equally silly. However,
de gustibus non disputandum est (Latin for "There's just
no arguing with Gus").
 
P

pete

Eric said:
It might be simpler to say that the function name "is"
a pointer to the function, since there is no context in
which it does anything other than "decay."

The function name remains an expression of a function type
when it is an operand of the address operator.
You can force a non macro invocation of a standard library
function by calling it like
(&putchar)('\n');
which wouldn't make sense if putchar were a pointer.
If a function name were a pointer expression,
then it would be a valid operand of the sizeof operator.
For those two reasons,
I would not say that a function name is a pointer.
 
E

Emmanuel Delahaye

Marlene Stebbins wrote on 02/05/05 :
#include <stdio.h>
/* declares a function which takes an argument
that is a pointer to a function returning an int */
void f(int (*fptr)());
/* function returning an int */
int ff(void);

int main(void)
{
f(ff); /* pass the address of ff to f */

return 0;
}

void f(int (*fptr)())
{
int a;
a = (*fptr)(); /* deref the func pointer */
printf("%d\n", a);
return;
}

int ff(void)
{
return 2345;
}

You've got it (jist add 'void' into the () and it's all fine). Now, try
the Simple Way:

#include <stdio.h>

typedef int F (void);

static int ff (void)
{
return 2345;
}

static void f (F * pf)
{
/* deref the func pointer */
int a = pf ();

printf ("%d\n", a);
return;
}

int main (void)
{
/* pass the address of ff to f */
f (ff);

return 0;
}

--
Emmanuel
The C-FAQ: http://www.eskimo.com/~scs/C-faq/faq.html
The C-library: http://www.dinkumware.com/refxc.html

"Clearly your code does not meet the original spec."
"You are sentenced to 30 lashes with a wet noodle."
-- Jerry Coffin in a.l.c.c++
 
K

Keith Thompson

Eric Sosman said:
It might be simpler to say that the function name "is"
a pointer to the function, since there is no context in
which it does anything other than "decay."

Almost. A function name decays (is implicitly converted) to a pointer
in most contexts. The exceptions are when it's the operand of a unary "&"
(&ff yields a pointer-to-function, not a pointer-to-pointer-to-function),
and when it's the operand of sizeof (sizeof ff is illegal; if it
decayed in that context it would yield the size of a function
pointer).

[snip]
This can also be written `a = fptr();' without the
parentheses and the asterisk. Some people prefer to write
the call as you've done it, saying that it draws attention
to the fact that a function pointer variable (rather than
a function identifier) is being used. I personally don't
buy that argument, noting that

(*printf)("Hello, world!\n");

is equally legitimate and (IMHO) equally silly. However,
de gustibus non disputandum est (Latin for "There's just
no arguing with Gus").

Note that
(*printf)("Hello, world!\n");
isn't exactly equivalent to
printf("Hello, world!\n");
since the latter could invoke a macro. That's a special rule for
functions declared in standard headers. But if you want to avoid the
macro and call the actual function, you can just use parentheses:

(printf)("Hello, world!\n");

Hmm. Actually, since printf() takes a variable number of arguments,
implementing it as a macro is not possible in C90; I'm not sure
whether it's possible in C99. But I digress.
 
L

Lawrence Kirby

The function name remains an expression of a function type
when it is an operand of the address operator.
You can force a non macro invocation of a standard library
function by calling it like
(&putchar)('\n');

That's true although in practice it tends to be written as

(putchar)('\n');
which wouldn't make sense if putchar were a pointer.

But, yes, your form makes this point.
If a function name were a pointer expression,
then it would be a valid operand of the sizeof operator.
For those two reasons,
I would not say that a function name is a pointer.

It is a very similar argument to that for array names. Array names have
array type but in many cases an array "value" is given as a pointer to the
array's first element, so in those cases it appears to be a pointer. But
it isn't and as with functions &array and sizeof array show its true
nature.

Lawrence
 
P

pete

Lawrence said:
That's true although in practice it tends to be written as

(putchar)('\n');


But, yes, your form makes this point.


It is a very similar argument to that for array names.
Array names have array type but in many cases an array "value"
is given as a pointer to the array's first element,
so in those cases it appears to be a pointer.
But it isn't and as with functions &array
and sizeof array show its true nature.

I would agree with Eric Sosman's statement to the extent
that the only use of a value of an expression of a function type,
is that a pointer can be derived from it.

What constitutes the value of an array type, is more complicated.
The value of an array type (even an incomplete array type)
can be converted to a pointer.
The value of an array type can initialize an array.
char array[] = "";
The value of an array type structure member,
is a byte by byte copy when the structure
is an operand of the assignment operator.
 
M

Michael Wojcik

Almost. A function name decays (is implicitly converted) to a pointer
in most contexts. The exceptions are when it's the operand of a unary "&"
(&ff yields a pointer-to-function, not a pointer-to-pointer-to-function),
and when it's the operand of sizeof (sizeof ff is illegal; if it
decayed in that context it would yield the size of a function
pointer).

I was mulling over the hypothesis that the & and * operators, when
applied to function names, simply did nothing, but your last example
made me think of a possible counterexample. Is the following a
strictly conforming expression?

sizeof &main;

Or, if you prefer the full-program version:

#include <stdio.h>
int main(void) {
printf("%lu\n", (unsigned long) sizeof &main);
return 0;
}

If it is, aside from that, are there any other cases where the & and
* operators have any effect when applied to a function name?
 
E

Eric Sosman

pete said:
I would agree with Eric Sosman's statement to the extent
that the only use of a value of an expression of a function type,
is that a pointer can be derived from it.

Although it's flattering to learn that someone agrees with
me, it is also a bit embarrassing -- because I have come to
*dis*agree with myself ;-)

I said there was no context in which a function identifier
doesn't "decay" to a pointer to the function, but Keith
Thompson has convinced me I was wrong. Specifically:

- In the context `&func', the `func' part does not behave
like a pointer-to-function. If it did, the entire
expression would be pointer-to-pointer-to-function --
but, strangely enough, both `func' and `&func' (and
even `&&&&&&&func') are pointer-to-function.

- In the context `sizeof func', the `func' part does not
behave like a pointer-to-function. If it did, the
expression would evaluate to the size of a function
pointer, but in actuality the expression produces a
diagnostic.

The second point is debatable: since the context is not
valid C at all, its odd behavior doesn't matter. One might
equally well make arguments about the array-indexing operator
in the context `[4]"Hello"'; interesting things might be said,
but they'd have little to do with C. But even if one rejects
the second point, the first seems unassailable: Keith is right
and I was wrong, R-O-N-G, wrong.
 
L

Lawrence Kirby

On Tue, 03 May 2005 10:57:05 -0400, Eric Sosman wrote:

....
I said there was no context in which a function identifier
doesn't "decay" to a pointer to the function, but Keith
Thompson has convinced me I was wrong. Specifically:

- In the context `&func', the `func' part does not behave
like a pointer-to-function. If it did, the entire
expression would be pointer-to-pointer-to-function --
but, strangely enough, both `func' and `&func' (and
even `&&&&&&&func') are pointer-to-function.

Using the && operator like this is a syntax error. But even
(& &func) is invalid because unary & requires an lvalue or function
designator as its operand, but it doesn't produce either as its result.

Lawrence
 
K

Keith Thompson

Eric Sosman said:
Although it's flattering to learn that someone agrees with
me, it is also a bit embarrassing -- because I have come to
*dis*agree with myself ;-)

I said there was no context in which a function identifier
doesn't "decay" to a pointer to the function, but Keith
Thompson has convinced me I was wrong. Specifically:

- In the context `&func', the `func' part does not behave
like a pointer-to-function. If it did, the entire
expression would be pointer-to-pointer-to-function --
but, strangely enough, both `func' and `&func' (and
even `&&&&&&&func') are pointer-to-function.

As Lawrence Kirby pointed out, this is illegal because the "maximal
munch" rule turns this into three "&&" operators followed by an "&"
operator; once you fix that, it's still illegal because &func isn't a
lvalue. But you can have &func, func, *func, **func, *****func, and
*&*&*&*&func.
- In the context `sizeof func', the `func' part does not
behave like a pointer-to-function. If it did, then
expression would evaluate to the size of a function
pointer, but in actuality the expression produces a
diagnostic.

The second point is debatable: since the context is not
valid C at all, its odd behavior doesn't matter. One might
equally well make arguments about the array-indexing operator
in the context `[4]"Hello"'; interesting things might be said,
but they'd have little to do with C. But even if one rejects
the second point, the first seems unassailable: Keith is right
and I was wrong, R-O-N-G, wrong.

I hate to force you to disagree with yourself even further, but here's
what the standard says:

C99 6.3.2.1p4:

A function designator is an expression that has function
type. Except when it is the operand of the sizeof operator(54) or
the unary & operator, a function designator with type "function
returning type" is converted to an expression that has type
"pointer to function returning type".

And footnote (54):

Because this conversion does not occur, the operand of the sizeof
operator remains a function designator and violates the constraint
in 6.5.3.4.

6.5.3.4p1 says:

Constraints

The sizeof operator shall not be applied to an expression that has
function type or an incomplete type, to the parenthesized name of
such a type, or to an expression that designates a bit-field
member.

It seems a little convoluted to make an exception to the rule for the
operand of a sizeof operator, only to make it a constraint violation
in a different section of the standard, but I think this is actually
the most straightforward way to do it. The rule is similar to the
rule for arrays, except that it makes sense to apply sizeof to an
array value.

[4]"Hello", on the other hand, is a syntax error, not a constraint
violation. (And as you know, 4["Hello"] is perfectly legal.)
 
P

pete

Michael said:
sizeof &main;

That's the size of a pointer.
If it is, aside from that, are there any other cases where the & and
* operators have any effect when applied to a function name?

The * operator always converts the function type expression
to a pointer, prior to operating on it.

These are all valid function calls:

(putchar)('\n');
(&putchar)('\n');
(*putchar)('\n');

In the first one, the function type expression,
is converted to a pointer, by the function call operator.

The second one has no conversions.

In the third one, the * operator converts the function type
expression to a pointer, then operates on it,
the result is an expression of function type
which is converted to a pointer by the function call operator.
 
P

pete

Eric said:
Although it's flattering to learn that someone agrees with
me, it is also a bit embarrassing -- because I have come to
*dis*agree with myself ;-)

I said there was no context in which a function identifier
doesn't "decay" to a pointer to the function, but Keith
Thompson has convinced me I was wrong. Specifically:

- In the context `&func', the `func' part does not behave
like a pointer-to-function. If it did, the entire
expression would be pointer-to-pointer-to-function --
but, strangely enough, both `func' and `&func' (and
even `&&&&&&&func') are pointer-to-function.

- In the context `sizeof func', the `func' part does not
behave like a pointer-to-function. If it did, the
expression would evaluate to the size of a function
pointer, but in actuality the expression produces a
diagnostic.

The second point is debatable: since the context is not
valid C at all, its odd behavior doesn't matter.

The fact that the function name is not a pointer, is the
reason why the function name is not a valid operand of sizeof.
Function pointers are object types and valid operands of sizeof.
So, if the point is whether or not function names are pointers,
then the sizeof issue is relevant.
One might
equally well make arguments about the array-indexing operator
in the context `[4]"Hello"'; interesting things might be said,
but they'd have little to do with C.

The array indexing operator converts the array name to a pointer,
just as the * operator does with both array names and function names.
 
K

Keith Thompson

pete said:
That's the size of a pointer.


The * operator always converts the function type expression
to a pointer, prior to operating on it.

No, the * operator doesn't do any conversions. The conversion occurs
before the * operator is invoked. Perhaps this is just a matter of
terminology, but in my opinion saying that the operator converts its
operand is misleading. The * operator expects a pointer operand; it
gets one because the operand is already a pointer.

Given
int n;
the expression *n is illegal. If the * operator had the ability to
convert its operand to a pointer type, *n would be equivalent to n.
You could argue, I suppose, that the * operator converts its operand
only in certain circumstances, but I don't see any support for this
idea in the standard.
These are all valid function calls:

(putchar)('\n');
(&putchar)('\n');
(*putchar)('\n');

In the first one, the function type expression,
is converted to a pointer, by the function call operator.

The expression putchar is of function type. Since it's not the
operand of a sizeof or "&" operator, it's implicitly converted to a
pointer-to-function. The parenthesized expression is a no-op, used
only for syntactic resolution. This pointer-to-function *then*
becomes an operand of the function call operator; since the function
call operator expects a pointer-to-function, no further conversion is
necessary.
The second one has no conversions.
Agreed.

In the third one, the * operator converts the function type
expression to a pointer, then operates on it,
the result is an expression of function type
which is converted to a pointer by the function call operator.

The expression putchar is of function type. Since it's not the
operand of a sizeof or "&" operator, it's implicitly converted to a
pointer-to-function. The "*" operator expects, and gets, a pointer as
its operand; it yields an lvalue designating the thing the pointer
points to. In this case, the result is an lvalue designating the
putchar function. This is, again, an expression of function type.
Since this is also not the operand of a sizeof or "&" operator, it's
again implicitly converted to a pointer-to-function type. The
parentheses again have no semantic effect. The function call operator
expects, and gets, a pointer-to-function as its operand.
 
P

pete

Keith said:
No, the * operator doesn't do any conversions. The conversion occurs
before the * operator is invoked. Perhaps this is just a matter of
terminology, but in my opinion saying that the operator converts its
operand is misleading.

N869
6.3 Conversions
[#1] Several operators convert operand values from one type
to another automatically.
 
P

pete

pete said:
Keith said:
No, the * operator doesn't do any conversions. The conversion occurs
before the * operator is invoked. Perhaps this is just a matter of
terminology, but in my opinion saying that the operator converts its
operand is misleading.

N869
6.3 Conversions
[#1] Several operators convert operand values from one type
to another automatically.

I think you're right. It would be accurate
to say that not being the operand of & or sizeof,
is what converts the function type expression to a pointer.
 
K

Keith Thompson

pete said:
Keith Thompson wrote: [snip]
No, the * operator doesn't do any conversions. The conversion occurs
before the * operator is invoked. Perhaps this is just a matter of
terminology, but in my opinion saying that the operator converts its
operand is misleading.

N869
6.3 Conversions
[#1] Several operators convert operand values from one type
to another automatically.

Hmm, that's an odd statement. I don't have access to my copy of the
standard at the moment; I'll take a look at the context later tonight.
Maybe it's refering to arithmetic promotions?
 
K

Keith Thompson

Keith Thompson said:
pete said:
Keith Thompson wrote: [snip]
No, the * operator doesn't do any conversions. The conversion occurs
before the * operator is invoked. Perhaps this is just a matter of
terminology, but in my opinion saying that the operator converts its
operand is misleading.

N869
6.3 Conversions
[#1] Several operators convert operand values from one type
to another automatically.

Hmm, that's an odd statement. I don't have access to my copy of the
standard at the moment; I'll take a look at the context later tonight.
Maybe it's refering to arithmetic promotions?

Yes, it's talking about the "usual arithmetic conversions".

C99 6.3p1:

Several operators convert operand values from one type to another
automatically. This subclause specifies the result required from
such an implicit conversion, as well as those that result from a
cast operation (an explicit conversion). The list in 6.3.1.8
summarizes the conversions performed by most ordinary operators;
it is supplemented as required by the discussion of each operator
in 6.5.

6.3.1.8, "Usual arithmetic conversions", covers things like the "*"
operator in 1.2 * 3 converting its right operand from int to double.
IMHO it would make more sense to say that the right operand is
converted to double because of the context in which it appears, not
that it's converted by the operator, but that's the wording chosen in
the standard.
 
P

pete

Keith said:
Keith Thompson said:
pete said:
Keith Thompson wrote: [snip]
No, the * operator doesn't do any conversions. The conversion occurs
before the * operator is invoked. Perhaps this is just a matter of
terminology, but in my opinion saying that the operator converts its
operand is misleading.

N869
6.3 Conversions
[#1] Several operators convert operand values from one type
to another automatically.

Hmm, that's an odd statement. I don't have access to my copy of the
standard at the moment; I'll take a look at the context later tonight.
Maybe it's refering to arithmetic promotions?

Yes, it's talking about the "usual arithmetic conversions".

C99 6.3p1:

Several operators convert operand values from one type to another
automatically. This subclause specifies the result required from
such an implicit conversion, as well as those that result from a
cast operation (an explicit conversion). The list in 6.3.1.8
summarizes the conversions performed by most ordinary operators;
it is supplemented as required by the discussion of each operator
in 6.5.

6.3.1.8, "Usual arithmetic conversions", covers things like the "*"
operator in 1.2 * 3 converting its right operand from int to double.
IMHO it would make more sense to say that the right operand is
converted to double because of the context in which it appears, not
that it's converted by the operator, but that's the wording chosen in
the standard.

I had mistakenly interpreted the standard to mean
that being the operand of operators other than sizeof or &,
was the condition which caused conversion. That's close,
because if a function name isn't the operand of sizeof or &,
then it's usually the operand of some other operator,
but not always.
I can only think of two cases where a function name would be converted
without being an operand:
Initialization of a function pointer
ptr = function_name;
and a no op statement
function_name;
 

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

Forum statistics

Threads
473,754
Messages
2,569,522
Members
44,995
Latest member
PinupduzSap

Latest Threads

Top