Shaun Xu

The Sheep-Pen of the Shaun


News

logo

Shaun, the author of this blog is a semi-geek, clumsy developer, passionate speaker and incapable architect with about 10 years’ experience in .NET and JavaScript. He hopes to prove that software development is art rather than manufacturing. He's into cloud computing platform and technologies (Windows Azure, Amazon and Aliyun) and right now, Shaun is being attracted by JavaScript (Angular.js and Node.js) and he likes it.

Shaun is working at Worktile Inc. as the chief architect for overall design and develop worktile, a web-based collaboration and task management tool, and lesschat, a real-time communication aggregation tool.

MVP

My Stats

  • Posts - 122
  • Comments - 622
  • Trackbacks - 0

Tag Cloud


Recent Comments


Recent Posts


Archives


Post Categories


.NET



When I'm playing some cut-edging technologies such as Node.js and AngularJS, I found that I need to review some basic knowledge of program language and skill. So these days I read some books about ANSI C, and reviewed some basic concepts such as data types, structures and pointers. And I spent days to understand the function pointer that was not very clear when I was in collage, with the skills I'm having from some modern languages.

 

Function Pointer is Simple

Many people said pointer is very hard to understand while the function pointer is the most. But I don't think so. In fact we are using function pointer every time we wrote a function. For example, in the code below I declared a function and invoked it.

   1: #include <stdio.h>
   2:  
   3: int add (int, int);
   4:  
   5: int main () 
   6: {
   7:     int x = 4;
   8:     int y = 7;
   9:     int result;
  10:  
  11:     result = add(x, y);
  12:     printf("%d + %d = %d\n", x, y, result);
  13:  
  14:     return 0;
  15: }
  16:  
  17: int add (int x, int y)
  18: {
  19:     return x + y;
  20: }

In fact when I defined the function "add" at the top of my code, I defined a function pointer variant named "add" with the type of "a function that has two integer arguments and returns an integer". And when I declared the function at the bottom I in fact assign a function to the function pointer variant "add".

When I invoked "add" function, ANSI C found the function address in memory and executed it, just as what it does against any pointers variants. Hence we can invoke this function as below.

   1: // result = add(x, y);
   2: result = (*add)(x, y);

Since "add" is a variant we can assign it to another variant and execute. In the code below I defined a function pointer variant named "op", assign "add" to it and executed it. Please note the declaration function pointer variant the name of variant between the function return type and arguments.

   1: int (*op)(int, int);
   2: op = add;
   3: result = op(x, y);

Also, we can pass a function pointer variant into another function as a parameter, or a function that returns a function pointer. This makes our code looks like functional programing. For example the code below will invoke the function pointers 10 times and return the summary of them.

   1: int run10times (int x, int y, int (*op)(int, int))
   2: {
   3:     int i;
   4:     int result = 0;
   5:     for (i = 0; i < 10; i++)
   6:     {
   7:         result += op(x, y);
   8:     }
   9:     return result;
  10: }

And we can invoked it by passing the "add" as the last parameter. In fact we passed a function pointer named "add" to it.

   1: int main () 
   2: {
   3:     int x = 4;
   4:     int y = 7;
   5:     int result;
   6:  
   7:     result = run10times(x, y, add);
   8:     printf("10 times of \'%d + %d\' = %d\n", x, y, result);
   9:  
  10:     return 0;
  11: }

If we have another function named "multiple" that multiples two parameters and returns, then we can pass this function to it, something likes functional programing.

   1: #include <stdio.h>
   2:  
   3: int add (int, int);
   4: int multiple (int, int);
   5: int run10times (int x, int y, int (*op)(int, int));
   6:  
   7: int main () 
   8: {
   9:     int x = 4;
  10:     int y = 7;
  11:     int result;
  12:  
  13:     result = run10times(x, y, multiple);
  14:     printf("10 times of \'%d * %d\' = %d\n", x, y, result);
  15:  
  16:     return 0;
  17: }
  18:  
  19: int add (int x, int y)
  20: {
  21:     return x + y;
  22: }
  23:  
  24: int multiple (int x, int y)
  25: {
  26:     return x * y;
  27: }
  28:  
  29: int run10times (int x, int y, int (*op)(int, int))
  30: {
  31:     int i;
  32:     int result = 0;
  33:     for (i = 0; i < 10; i++)
  34:     {
  35:         result += op(x, y);
  36:     }
  37:     return result;
  38: }

 

"void *" is Magical

Function pointer in ANSI C is strong typed. As we can see in the code above, a function pointer type was combined by the return type and arguments' type. But there's a special type in ANSI C we can use to make the function pointer more flexible, but more tricky as well, which is "void *".

The "void *" means a pointer that can be pointed to any types. This means we can covert any types of a pointer to "void *". Let's create a new function named "intaddto" which add the seconds parameter to the first one. In order to pass the result back we need to use integer pointer.

   1: #include <stdio.h>
   2:  
   3: void intaddto (int *, int *);
   4:  
   5: int main () 
   6: {
   7:     int x = 4;
   8:     int y = 7;
   9:  
  10:     intaddto(&x, &y);
  11:     printf("%d\n", x);
  12:  
  13:     return 0;
  14: }
  15:  
  16: void intaddto (int *x, int *y)
  17: {
  18:     *x += *y;
  19: }

Since "void *" can be converted from any types of pointer, in the code below I defined another function pointer named "op" with the arguments' type as "void *" and assign our "intaddto" to it, and invoked "op" that returned the same result.

   1: void (*op)(void *, void *);
   2: op = (void (*)(void *, void *))intaddto;
   3: op(&x, &y);

With this feature we can change our "run10times" function a bit, so that it can handle any types, make it looks like a generic function.

It just looks like a generic function but in fact it's NOT. We can use template feature to implement a fully generic function in C++.

   1: void run10times (void *x, void *y, void (*op)(void *, void *))
   2: {
   3:     int i;
   4:     for (i = 0; i < 10; i++)
   5:     {
   6:         op(x, y);
   7:     }
   8: }

Then we can pass the "intaddto" function pointer to it as below.

   1: void (*op)(void *, void *);
   2: op = (void (*)(void *, void *))intaddto;
   3: run10times(&x, &y, op);

Now let's add another function named "dbladdto" that add the second double parameter to the first double parameter.

   1: void dbladdto (double *x, double *y)
   2: {
   3:     *x += *y;
   4: }

Then we can invoke "run10times" function by passing "dbladdto" as its last parameter so that it can handler double data type.

   1: double dx = 3.14159;
   2: double dy = 2.71828;
   3: run10times(&dx, &dy, (void (*)(void *, void *))dbladdto);
   4: printf("%g\n", dx);

 

Summary

In this post I tried to described the function pointer in ANSI C. Function pointer is basically one kind of pointer variant. It's hard to understand just because in ANSI C, we defined a function pointer alone with the definition of the function, and use it through the function name. If we back to the basic, it's the same to invoke a function by name or by the underlying pointer variant.

"void *" is a special type of pointer that can be assign by any types' of pointer. With this feature we can build a function that allows any kinds of parameter types.

The full sample code is listed below.

   1: #include <stdio.h>
   2:  
   3: void intaddto (int *, int *);
   4: void dbladdto (double *, double *);
   5: void run10times (void *, void *, void (*)(void *, void *));
   6:  
   7: int main () 
   8: {
   9:     int x = 4;
  10:     int y = 7;
  11:  
  12:     void (*op)(void *, void *);
  13:     op = (void (*)(void *, void *))intaddto;
  14:     run10times(&x, &y, op);
  15:     printf("%d\n", x);
  16:  
  17:     double dx = 3.14159;
  18:     double dy = 2.71828;
  19:     run10times(&dx, &dy, (void (*)(void *, void *))dbladdto);
  20:     printf("%g\n", dx);
  21:  
  22:     return 0;
  23: }
  24:  
  25: void intaddto (int *x, int *y)
  26: {
  27:     *x += *y;
  28: }
  29:  
  30: void dbladdto (double *x, double *y)
  31: {
  32:     *x += *y;
  33: }
  34:  
  35: void run10times (void *x, void *y, void (*op)(void *, void *))
  36: {
  37:     int i;
  38:     for (i = 0; i < 10; i++)
  39:     {
  40:         op(x, y);
  41:     }
  42: }

 

Hope this helps,

Shaun

All documents and related graphics, codes are provided "AS IS" without warranty of any kind.
Copyright © Shaun Ziyan Xu. This work is licensed under the Creative Commons License.

Comments

Gravatar # re: My Understanding of Function Pointer in ANSI C
Posted by Mickey on 1/9/2016 4:19 AM
Hello,
is there any chance to do this instead:
void* (*op)(void *, void *);

meaning a pointer to a function that return a (int) or a (double) ?

I tried but it doesn't seem possible....
Post A Comment
Title:
Name:
Email:
Comment:
Verification: