22

I'm a bit confused as to how I would pass a pointer to a pointer function. I have a function that takes a pointer to a function which I understand without problem (ExecBlock). But I'm given another prototype of a function (ExecBlock2) which takes the dereferenced-pointer (I'm not sure exactly what it is) and also takes the argument if passed function has any. If someone could explain the precedence and exactly what dereferencing a pointer function would do. Isn't that just passing the function itself?. What does (void *) do in this case?

int ExecBlock (void (*f) (void), int isSet)
{
    return ExecBlock2( HOW TO PASS HERE? , NULL, isSet);
}

int ExecBlock2(void *(*f)(void *), void *arg, int isSet)
{
    ... more code
}
NAND
  • 663
  • 8
  • 22
ThePedestrian
  • 819
  • 2
  • 11
  • 22

2 Answers2

49
void (*f) (void)

means pointer to function with no arguments returning void.

void *(*f)(void *)

means pointer to function taking a void pointer and returning a void pointer.

Since the types are different, the compiler will not allow you to pass one to the other without casting. (Note that casting is not really the right answer here, and as @detly points out, results in undefined behavior.)

As for dereferencing pointers to functions, you don't have to explicitly put a "*" before a function pointer to call it. For instance, you could call your function pointer f just by doing

f();

A function pointer example

Say you have a function f, which you'd like to pass to a function called takes_a_function. takes_a_function will probably have a type like

void takes_a_function(void (*f)(void *data), void *data);

Notice how there are two arguments to takes_a_function, a function pointer, and a void pointer to some data. Also note that the function f happens to take a void pointer as an argument. The idea is that you can pass the data to takes_a_function, and it will pass it along to f. For example, takes_a_function could be defined like

void takes_a_function(void (*f)(void *), void *data) {
  f(data);
}

Now, let's write a function to pass to takes_a_function. Our function will just print an int that is passed to it.

void prints_an_int(void *data) {
  // The idiom for converting a void pointer to another kind
  // of pointer.  NO NEED TO CAST.  Note this behavior is only
  // defined if the pointer data really does point to an int.
  int *i = data;
  printf("%d", *i);
}

int i = 0;
takes_a_function(prints_an_int, &i);

A couple of key points about this example:

  • prints_an_int has the same type as the function pointer expected by takes_a_function. No need to cast.
  • There's no need to use the & operator to create a reference to a function. This is why we can pass prints_an_int to takes_a_function directly. But we could also say takes_a_function(&prints_an_int, &i), and it would be the same.
  • void* basically means "pointer to unknown type." To actually do anything with it, you must assign a variable of type void* to another pointer variable whose type you expect. This is only guaranteed to work if you actually pass in the correct pointer type! In this example, we can assign data to an int*, since data really does point to an int. If you want more data than just an integer, a common pattern is to create your own struct type which includes all the fields you want, and pass that instead.
  • As a special case, the compiler does not require you to cast when assigning void pointers to other pointers and vice-versa. But again, you only get defined behavior if you eventually convert a void pointer back to the correct type.
Chris Rice
  • 1,390
  • 11
  • 16
  • *"you cannot pass one to the other without casting."* No! You cannot pass one to the other at all in a valid C program - it is undefined behaviour. – detly Oct 17 '13 at 03:44
  • 1
    @detly fair enough, I'll edit. I mean to say, the _compiler_ will not allow you to pass one to the other without casting. Not to say that the result is defined behavior. – Chris Rice Oct 17 '13 at 03:47
  • So what 'void *(*f)(void *)' really means is 'void * ((*f)(void *))' and then to pass it, I would just do ExecBlock2(f, ....). Correct? But wait, what happens to passing (void *) arguments? – ThePedestrian Oct 17 '13 at 03:50
  • 1
    @ThePedestrian, you're correct about the precedence and placing of parenthesis. But you cannot pass f to ExecBlock2 since ExecBlock2 is expecting a different type. – Chris Rice Oct 17 '13 at 03:53
  • So I cannot do the nesting like where I call ExecBlock2 inside of ExecBlock because ExecBlock2 expects a function that must return a (void *)? Correct? I would have to cast is as (void *). – ThePedestrian Oct 17 '13 at 03:58
  • 1
    @ThePedestrian The problem with casting is that the compiler assumes you completely know what you are doing, and it will stop complaining and compile your code. But there is a good reason that it was complaining in the first place. It is a better idea to re-think why you are trying to pass an incompatible type, and to engineer a better solution. I'll edit my answer to include a common pattern for function pointers. – Chris Rice Oct 17 '13 at 04:06
  • 1
    You are right. I just found this out the hard way. It seems like I would need to create some sort of stub in order to execute my function properly. – ThePedestrian Oct 17 '13 at 04:11
  • @ChrisRice Thank you so much for the further edit. It helps so much and clarifies things! – ThePedestrian Oct 17 '13 at 04:25
  • First class answer now :) – detly Oct 18 '13 at 11:02
1

For example you have a function

void * abc(void *)
{
//... 
}

int ExecBlock (void (*f) (void), int isSet)
    {
        return ExecBlock2( abc , NULL, isSet); //use name of the function which have void * as parameter type list and return type  void *   
        }

int ExecBlock2(void *(*f)(void *), void *arg, int isSet)
{
    ... more code
}  

See function-pointers

Gangadhar
  • 10,248
  • 3
  • 31
  • 50