CptWhoWent
CptWhoWent

Reputation: 33

What does int (*f[])(int*) represent?

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

Answers (7)

haccks
haccks

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/or volatile keyword is next to a type specifier (e.g. int, long, etc.) it applies to the type specifier. Otherwise the const and/or volatile 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

Kaz
Kaz

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

Grzegorz Szpetkowski
Grzegorz Szpetkowski

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

Marcus M&#252;ller
Marcus M&#252;ller

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

Vlad from Moscow
Vlad from Moscow

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

pmg
pmg

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

Some programmer dude
Some programmer dude

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

Related Questions