Reputation: 33
This is one of the exam questions and the goal is to determine what the program will write. I am really confused about what int (*f[])(int*) = {f1, f2, f2, f1 };
is. I taught that it may be an array whose elements are results of the functions in the brackets and those results are addresses that point on to an int.
Could you maybe explain it to me please?
And as well, what parameter does the function f[i++]
get in
for (i=0; i<2; a += fx(f[i++], a));
Problem:
int f2(int *p) { return *p-- + 2; }
int f1(int *q) { return ++*q; }
int fx(int (*pf)(int*), int q) {
int t = q;
while ((t = pf(&q)) % 2) q+=2;
return t;
}
#include <stdio.h>
void main() {
int a = 4, b = 3, *p = &b, i;
int (*f[])(int*) = {f1, f2, f2, f1 };
for (i=0; i<2; a += **fx(f[i++]**, a));
printf("%d", a + *p);
}
Upvotes: 3
Views: 7773
Reputation: 106012
It declares f
as an array of pointers to function that returns int
and take int *
as an argument.
There is a precedence rule for understanding complex declarations which is discussed in the book Expert C Programming: Deep C Secrets:
The Precedence Rule for Understanding C Declarations
A. Declarations are read by starting with the name and then reading in precedence order.
B. The precedence, from high to low, is:
B.1. parentheses grouping together parts of a declaration
B.2. the postfix operators:
parentheses()
indicating a function, and
square brackets[]
indicating an array.B.3. the prefix operator:
the asterisk denoting "pointer to".
- C. If a
const
and/orvolatile
keyword is next to a type specifier (e.g.int
,long
,etc
.) it applies to the type specifier. Otherwise theconst
and/orvolatile
keyword applies to the pointer asterisk on its immediate left.
Therefore, it goes like:
f -- f (A)
f[] -- is an array (B.1)
*f[] -- of pointers to (B.3)
(*f[])( ) -- function (B.2)
(*f[])( int * ) -- that expects a pointer to an int as an argument
int (*f[])( int* ) -- and return an int
I would suggest to avoid spiral rule as it fails in some cases, for example in case of int* a[10][15];
.
Upvotes: 4
Reputation: 58578
Unlike the usual teaching method, I suggest you start from the outside in. Recognize the general structure of the declaration and proceed by refinement: replace the coarse-grained "blobs" that you see with more detailed ones. In other words, traverse the syntax from the root of the syntax tree to the leaves, rather than trying to pick out the right leaf and working bottom up.
The general structure of a declaration (that has a single declarator) is:
TYPE WHATEVER;
WHATEVER
is being declared, related to TYPE
.
Now note that TYPE
is int
, so WHATEVER
is of type int
. And WHATEVER
has the general shape (W1)(W2)
: two syntactic units in parentheses, whatever 1 and whatever 2:
int (W1)(W2);
Here, W1
is what is being declared, and it is a function returning int
, which takes a W2
parameter list. The latter actually denotes int *
:
int (W1)(int *);
Thus W1
is a function returning int
which takes int *
. But W1
is actually *W3
, which makes W3
a pointer to W1
's type.
int (*W3)(int *);
W3
is a pointer to a function returning int
which takes int *
.
And W3
is actually f[]
, so f
is an array of unspecified size of W3
's type:
int (*f[])(int *);
f
is an array of unspecified size of pointers to function returning int
which takes int *
.
Tip: How do we know that W1
isn't actually W3[]
where W3
is then *f
? This is because the type construction operator [...]
is syntactically similar to the postfix array indexing operator [...]
and the type constructing *
is similar to the dereferencing unary operator *
. The unary operator has a lower predecence than the postfix operator. When we see *X[]
we know it means *(X[])
and not (*X)[]
. The symbols *X
do not form a syntactic unit in that phrase. If we want the second meaning, we have to use the parentheses.
The syntax tree is:
declaration
| |
+------------+ +----------------+
| |
specifier-qualifier list -- TYPE declarator -- WHATEVER
| | |
int +-----+ |
| |
function -- W1 params -- W2
| |
pointer -- W3 (int *)
|
array -- f
The helpful symbols like W3
are just labels for the nodes as we traverse from root to leaves. The name being declared in a C
declaration is somewhere at the bottom of the tree, unlike in some other languages. As go deeper into the tree, we are actually emerging out of the type, so finally when we arrive at the bottom, we know the most important things: f
is being declared, and overall it is an array of something.
Upvotes: 1
Reputation: 37914
It essentially defines an array of function pointers. The following:
int (*f[])(int*) = {f1, f2, f2, f1};
combines definition with array initializer. That is why you can omit size of an array, since it is deduced from initializer. The type of each element is:
int (*)(int*)
which is function pointer for function that takes one argument of type int *
and returns int
.
The for
loop's body consist of empty statement. You may rewrite it for better clarity as:
for (i = 0 ; i < 2; a += fx(f[i++], a))
;
There are two iterations for i = 0
and i = 1
. Each time a
is incremented by result of function call:
fx(f[i++], a)
First argument is an address of function, that is stored in f
array. It is f[0]
and f[1]
respectively (i.e. f1
and f2
). You might use equivalent form:
fx(&f[i++], a)
but there would be no real difference (it is just matter of preference).
If it looks too strange or complex for you, then you might rewrite for
loop as:
for (i = 0 ; i < 2; a += fx(f[i], a), i++)
;
as well as:
for (i = 0 ; i < 2; i++)
a += fx(f[i], a);
Upvotes: 0
Reputation: 36346
Let's take that apart with the help of the awesome C gibberish to English website:
int (*f[])(int*)
declare f as array of pointer to function (pointer to int) returning int
f
is an array of function pointers.
hence, using the {}
array initializer to store the functions in it is pretty clear.
Upvotes: 2
Reputation: 310970
This
int (*f[])(int*) = {f1, f2, f2, f1 };
is a declaration of an array of function pointers of type int( int * ) that is of functions that have return type int
and one parameter of type int *
.
As f
is an array then f[i++]
is an element of the array that has one of the values f1, f2 and so on.
This function
int fx(int (*pf)(int*), int q);
has two parameters: a pointer to a function that has type int( int * ) and an object of type int.
So this expression
fx(f[i++], a)
is a call of the function that accespts as arguments one of the function pointers f1, f2 and so on and object a
Upvotes: 0
Reputation: 108978
Applying the clockwise/spiral rule
int (*f[])(int*)
^ f is
int (*f[])(int*)
^^^ f is an array
int (*f[])(int*)
^^^^^^ f is an array of pointers
int (*f[])(int*)
^^^^^^^....^ f is an array of pointers to functions
int (*f[])(int*)
^^^^^^^^^^^^ f is an array of pointers to functions
that accept a pointer to int
int (*f[])(int*)
^^^^^^^^^^^^^^^^ f is an array of pointers to functions
that accept a pointer to int and return a int value
Upvotes: 14
Reputation: 409176
Following the clockwise/spiral rule we will see that f
is an array of pointers to functions, where each function in the array is taking an int*
argument and returning an int
.
The initialization simply initializes this array with some function pointers.
Knowing what f
is should help with the other problem.
Upvotes: 0