Pointers and References – Part 4 (Hacker Stuff) ;)

Ok, guys … party’s over. Some of you have requested some more in-depth stuff on pointers, so here we go: Function Pointer Arrays.

Depending on your previous programming experience, Function Pointer Arrays (FPA) may seem a bit nerdy to you. Nevertheless, it is basic knowledge to anyone who seriously intends to become a hacker. 😉

FPAs allow you to call functions based on the value of a numeric option. To get a better understanding of what this means, let’s consider this simple and traditional (non FPA) example.

#include <stdio.h>

int add( int a, int b ) {
  return a + b;
}

int mul( int a, int b ) {
  return a * b;
}

int main( int argc, char* argv[] ) {

  unsigned int num1, num2, choice;
  printf( "Enter 1st number: " );
  scanf( "%d", &num1 );
  printf( "Enter 2nd number: " );
  scanf( "%d", &num2 );
  printf( "Your choice (0=add, 1=multiply): " );
  scanf( "%d", &choice );

  int result = 0;
  if( choice == 0 ) result = add( num1, num2 );
  if( choice == 1 ) result = mul( num1, num2 );

  printf( "The result is %d.\n", result );

  return 0;

}

As you can surely see from the code, this program asks the user to enter two numbers (num1 and num2) and a numeric option (choice). Depending on the option’s numeric value, the two numbers will either be added or multiplied. Both operations, addition and multiplication, have been outsourced to two separate functions: add(…) and mul(…). The user’s input is not checked for validity, because I wanted to keep this example to be as simple as possible.

When teaching this subject in class, sometimes I would show this example to my students and ask: “Can anyone of you think of alternative ways to call different functions depending on the value of a numeric option?” The most common answer I’d get from my students is this: “Of course! We could use switch … case statements!”

There is nothing wrong with this answer, but using switchcase is only one alternative way.

What most hobbyists, script kiddies, and first semesters would not come up with is the following solution.

#include <stdio.h>

int add( int a, int b ) {
  return a + b;
}

int mul( int a, int b ) {
  return a * b;
}

int main( int argc, char* argv[] ) {

  int (*func_ptr_array[ 2 ]) ( int param1, int param2 );

  func_ptr_array[ 0 ] = add;
  func_ptr_array[ 1 ] = mul;

  unsigned int num1, num2, choice;
  printf( "Enter 1st number: " );
  scanf( "%d", &num1 );
  printf( "Enter 2nd number: " );
  scanf( "%d", &num2 );
  printf( "Your choice (0=add, 1=multiply): " );
  scanf( "%d", &choice );

  int result = (*func_ptr_array[ choice ]) ( num1, num2 );

  printf( "The result is %d.\n", result );

  return 0;

}

At this point, I always pause my lecture for a moment to allow my students re-hinge their previously dropped jaws. 😉

Now, let’s take a closer look on how this program works.

The lines that cause the option based function call are printed in bold type. In the first of these lines, a function pointer array (FPA) is declared. You can imagine this to be a simple one-dimensional array of pointers, similar to the char* argv[] array in the main() function header.

  int (*func_ptr_array[ 2 ]) ( int param1, int param2 );

The only notable thing about it is this: The pointers we are going to store in this array will point to program code as opposed to data. (In a pure Harvard architecture machine, function pointers refer to what is called instruction memory).

More precisely, the pointers that we are going to store in FPAs are start addresses of functions. The size of the array here in this example is 2, because there are two different functions we want to refer to, add(…) and mul(…).

The data type of the FPA corresponds to the return type of the functions. We also need to tell the compiler that each of the functions has two int parameters.

In the next two lines, we assign the storage locations of the functions to the elements of the previously declared FPA. Note, that similar to array names in C, the function’s names already represent their addresses. So add basically stands for “memory address where the implementation of the add function is located”.

  func_ptr_array[ 0 ] = add;
  func_ptr_array[ 1 ] = mul;

We have now created an array of pointers to both functions. This allows us to “invoke” both elements in pretty much the same way as we invoke functions directly.

int result = (*func_ptr_array[ choice ]) ( num1, num2 );

What needs to be done is to choose a specific element from the FPA, to dereference it, and to use it like a function name. You can pass parameters and retrieve the return value as usual. It is just the function name that has become “dynamic”. The line above can now have two different meanings, depending on the value of choice:

int result = add ( num1, num2 );

or

int result = mul ( num1, num2 );

Using FPAs allows you to do really fancy stuff. For example, you could use an n-dimensional FPA to invoke functions based on a whole set of options, or you could create a function with more than one implementation (similar to interfaces in OOP). Be aware, however, that function pointers can be as error prone as any other kind of pointers. Especially(!) when working with function pointers, keep your fingers off fiddling with pointer arithmetic … except you’re 200% sure about what you are doing. Jus’ sayin’ …

Enjoy,

— Andre M. Maier

Advertisements

About bitjunkie

Teacher, Lecturer, and BITJUNKIE ...
This entry was posted in Pointers, Programming Essentials, Uncategorized and tagged , , , . Bookmark the permalink.