Function Pointers--Why?

Okay, I'm a decent programmer, but I'm a little rusty. I know about function pointers, and the syntax and some basic implementation.

But I'm only really coming across them recently, and all the sites I've seen which cover them have been ultimately unhelpful in educating me as to WHY they are used.

If anyone can offer some good reasons to use a function pointer, I'd greatly appreciate it.

Comments

  • In C++, you don't really need to use them, as there are safer and better methods in that language.

    In C, they are typically used for generic programming. For example, if you have your own data type like a linked list, stack, binary tree or whatever, you can make generic algoriths by using a function pointer.

    Like this (pseudo code):

    [code]

    typdef struct /* some sort of custom type */
    {
    MyNode node[x];
    ...

    } MyList;

    void mylist_execute (MyList* list, void(*fptr)(MyNode*))
    {
    for(i=0; i < size of list; i++)
    {
    fptr(list->node[i]);
    }
    }

    void print(MyNode* node)
    {
    printf("...", node);
    }

    /* implement whatever fancy functions you need... */

    ...

    mylist_execute(list, print);
    mylist_execute(list, sum);
    mylist_execute(list, clear);

    and so on...[/code]


    Some C library functions like qsort() use function pointers in this way.

    Another example is DLL functions in Windows programming, where you ask the DLL for a function name and get a function pointer in return, which you can use to call the function.

    Function pointers are also useful in low-level programming. For example, the only way you can declare a CPU's interrupt vector table in C, is as an array of function pointers.

    There are plenty of uses for them, the use is "generic C programming".
  • [color=Blue]The function pointers are also making code faster! For example, imagine that you are writing the disassembler. When disassembler reads a code stream - this stream follows byte-by-byte and the decision on what to do with the byte comes from the byte value. Without the function pointers, the disassembler code looks like that:[/color]
    [code]
    BYTE opcode = ;

    if (opcode==0x00) do_action_on_opcode_00 ();
    else if (opcode==0x01) do_action_on_opcode_01 ();
    else if (opcode==0x02) do_action_on_opcode_02 ();
    ...
    else if (opcode==0xFF) do_action_on_opcode_FF ();
    [/code]
    [color=Blue]In computer science: the more IF() statements you execute in a code flow - the slower that code gets. That code above ^^^ is extremely slow!! You can't get slower that that! For example to get to decide on what to do with opcode 0xA0 you need to jump through IF()'s about 0xA0 times. Not a good solution.

    However, with function pointers - the code gets blazingly fast and simple![/color]
    [code]
    [color=Green]// Build a table of 256 pointers to deciding functions:[/color]
    FUNCPTR array [256] =
    {
    do_action_on_opcode_00,
    do_action_on_opcode_01,
    ...
    do_action_on_opcode_FF
    };

    [color=Green]// And then use the BYTE value itself as an index to table
    // for calling the function pointer in that slot![/color]

    BYTE opcode = ;
    array [opcode] ();

    [color=Green]// To be even faster - use global variables as parameters
    // for these functions - do not pass anything as parameters.
    // It will only slow down the disassembler engine![/color]
    [/code]
  • That's a good example, the same method is also commonly used when you want to optimize away sluggish switch() statements. The requirement is that all numbers checked are adjacent.

    (If they aren't adjacent, func pointers won't be much of an improvement)

    [code]switch(x)
    {
    case SOMETHING:
    run_some_func();
    break;

    case SOMETHING_ELSE:
    run_some_other_func();
    break;

    ...
    }





    FuncPtr array [N] =
    {
    run_some_func,
    run_some_other_func
    };

    array[x]();[/code]

    Actually, good compilers do that particular optimization for you. Then you will never see it, but behind the lines of your switch(), if{} else if{} etc there might actually be an array of function pointers.

    If the compiler isn't that smart, the programmer should know of this optimization. It is particulary useful when you want to optimize a complex WindowsProc in Windows API programming: those programs tend to use long-winded, switch-statements.
  • Aha, now it's starting to make sense!

    So, for instance, if I was sending a transmission, and wanted to send it one character at a time, in a specific order, I could use

    FuncPtr[msglength]

    And then for each index of FuncPtr I could send a different part of the message. Then I could simply increment the address, thus activating the next "piece" of the function?

    I only ask this because I've done state machines plenty, and it seems like a rather good way of simplifying some of the issues that can go along with them, especially when dealing with longer state machines.
  • Yes indeed, function pointer arrays are excellent for state machines. You can make an enum with names for each state and use that enum for indexing.
Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Categories