Function Pointers

Discussion in 'Mac Programming' started by Cromulent, Apr 19, 2008.

  1. Cromulent macrumors 603

    Cromulent

    Joined:
    Oct 2, 2006
    Location:
    The Land of Hope and Glory
    #1
    I'm trying to fill in a few gaps in my knowledge of C and have started to look into function pointers. I understand how they work and what the process is but I can't for the life of me think of an example that I could play around with where they are really useful.

    What is the main use of function pointers? The tutorial I have been reading is rather sparse on information (I mean it tells you how to use them and what the syntax is but it does not give me many ideas for simple programs I could write to get more comfortable with them).

    Basically from what I have read they seem handy for replacing switch and if statements and for callbacks (something I also need to look into). I think I'm missing out on not understanding them as they seem a pretty important aspect of C.
     
  2. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #2
    They are pretty rare in my experience. I maintain many thousands of lines of C at work, and there was one instance that used function pointers that was recently removed.

    What I would CONSIDER using them for:

    Faking structs to be more object like. If I have a few structs and each one needs to act differently (like polymorphism, etc.), there could be a "toString" function pointer that displays that struct in a particular way. You could have a list of functions that act on a node type in a node so they are handy, and the node could be used for different types of lists, etc. These are somewhat contrived.

    Conditionally acting on something when the call needs to be made elsewhere. This is also rare, but may be needed for things like callbacks as you mentioned. Say I am checking a condition in function foo(). i know that based on this condition i may need to call barA() or barB() later, but not until I am in function foo2(). I could pass the function pointer to foo2() and it could use it. This is, again, somewhat contrived. You could just pass a "boolean" to foo2() and act based on that. When you get 10 cases it MIGHT be better to set a function pointer instead of passing a "key" and using a switch.

    There are probably other (better) examples, but these were the cases I could think of.

    -Lee
     
  3. -Alan- macrumors member

    Joined:
    Mar 10, 2007
    #3
    It's been ages since I programmed, but what comes to mind is passing the address of a function into a function.

    Edit: I guess what I said doesn't communicate well. Suppose you don't know which function you need to pass into a function. That's where a pointer comes in handy.
     
  4. yeroen macrumors 6502a

    yeroen

    Joined:
    Mar 8, 2007
    Location:
    Cambridge, MA
    #4
    A good example of where function pointers are used are the Standard C Library sort functions: qsort, heapsort, and mergesort. These each take as an argument a pointer to a comparison function that you may define for your own types.

    For example if you've defined a datatype, implemented as a struct, upon which some ordinal relation of "less than, greater than" holds, you'd define your own comparison function allowing you to determine which struct is "less than" another. You'd then pass a pointer to that function to the sorting routine so it knows how to perform comparisons of elements.

    Function pointers are also how the C++ compiler implements virtual functions by creating a table (the vtable) of function pointers (and virtual functions are related to your remark about simplifying type-switching).

    And of course, let's not forget event-driven GUI programming where you define callbacks for widgets.
     
  5. Cromulent thread starter macrumors 603

    Cromulent

    Joined:
    Oct 2, 2006
    Location:
    The Land of Hope and Glory
    #5
    Thanks for the tips.

    Interesting that you mention things like pseudo object orientated uses for them.

    Hmm, sounds interesting that you can do things like pass a function pointer to a function and then use the pointer to change what the function calls depending on the state of the function.

    The question is though, how efficient are function pointers? I assume they are pretty fast as they are just the equivalent of the jump statement is assembly I assume?
     
  6. Catfish_Man macrumors 68030

    Catfish_Man

    Joined:
    Sep 13, 2001
    Location:
    Portland, OR
    #6
    Quite fast, yeah. Somewhat faster than an Objective-C message send or C++ virtual function call.
     
  7. Gelfin macrumors 68020

    Gelfin

    Joined:
    Sep 18, 2001
    Location:
    Denver, CO
    #7
    Well, since a C++ vftable and an Objective-C class method table are, under the hood, pretty much just ways of managing collections of function pointers, you'd kind of expect this.

    This tutorial page used to be at www.function-pointer.org, which I always thought was pretty cool. It's a decent site.

    Most times you use a function pointer, it's to interface with somebody else's API. They'll give you a function prototype, you'll use it, and you'll hand off the name of your function as an argument. It's so simple you could do it without really even realizing you were using function pointers at all. It's when you have to implement the API yourself to support function pointers that you have to understand how it works.
     
  8. AlmostThere macrumors 6502a

    #8
    You might also encounter function pointers through dynamic linking, e.g. accessing libraries through dlopen on UNIX or getProcAddress on Windows.

    I think most of the uses have been addressed above but I would also add that it sometimes be more expressive or flexible to use function pointers. A simple example would be the use of transform in C++, which stores the return value from a function called on each item in one range into another (the benefits of this perhaps become more apparent if you have a complex series of loops for iteration, e.g. range within a multi-dimensional array, and want to call different functions on the items).

    In addition to, or maybe rather than, thinking of function pointers as pseudo-OO, you might think of them as pseudo functional programming.
     
  9. Sayer macrumors 6502a

    Sayer

    Joined:
    Jan 4, 2002
    Location:
    Austin, TX
    #9
    I find it hard to believe you haven't used them yet. Although in Cocoa frameworks you use delegate methods and subclassing instead of function pointers. One are you may hit them is in the lower level of OS X like Core MIDI which isn't a Cocoa framework.

    They typically have a defined type like a delegate method and are called from some other chunk of code - even other threads in the same app (Core MIDI)

    No big deal.
     
  10. pilotError macrumors 68020

    pilotError

    Joined:
    Apr 12, 2006
    Location:
    Long Island
    #10
    In the old days...

    I used them heavily when doing direct hardware writes to video cards. Different video cards had different addressing needs and after interrogating the hardware it would be assigned an appropriate set of functions, this way the higher level API's wouldn't need to know about what it was using.

    I haven't used them since the days of writing DOS TSR's (Terminate Stay Resident) programs...

    Edit: Actually, I do have call backs and pre/post processing of messages that use them. Duhhh. Never really thought much about it lately...
     
  11. yeroen macrumors 6502a

    yeroen

    Joined:
    Mar 8, 2007
    Location:
    Cambridge, MA
    #11
  12. MrStevieP macrumors newbie

    Joined:
    Feb 27, 2008
    #12
    State machine and call backs are the most common uses that I have come across. There is also quite alot of instances in Linux kernel/device driver development where they used extensively.

    I think generally you wont use them in day to day C unless you want to do a state machine, hardware control or maybe graphics programming (Open GL uses them).

    Whilst it has been mentioned that you can make C more C++ like using them, I wouldnt recommend it. If you are trying to impose object type behaviors, then it sounds like you really need objects proper and hence Objective-C or C++ or A.N other object derived language.
     
  13. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #13
    I was stretching on that example. The only time i'd consider carrying function pointers extensively with structs would be on a platform where an OO language is not an option. That's getting fewer and fewer, but sometimes C is all you have.

    I don't think it's too far-fetched to pass a struct around that has a pointer to its comparator so it can easily be accessed for sorting or quick comparison to another struct of the same type.

    -Lee
     
  14. phjo macrumors regular

    Joined:
    Jan 8, 2008
    #14
    I do use function pointers in my plotting framework... Just give the framework a pointer to a function you want to draw the graph, and let the framework do the work for you... Stupid example there :
    http://www.mpkju.fr/~graphview/page1/page15/page15.html

    In my experience, it is quite fast.

    phjo
     
  15. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #15
    The speed of function pointers came up, and i thought i'd post an example. There is a trivial difference in calling a function via a pointer vs. a direct call. Here are two source programs:

    testptr.c
    Code:
    #include <stdio.h>
    
    void hello_world();
    int main(int argc, char *argv[]) {
      void (*hello_call)() = NULL;
      hello_call = hello_world;
      hello_call();
      return 0;
    }
    
    void hello_world() {
      printf("Hello, World!\n");
    }

    testcall.c
    Code:
    #include <stdio.h>
    
    void hello_world();
    int main(int argc, char *argv[]) {
      hello_world();
      return 0;
    }
    
    void hello_world() {
      printf("Hello, World!\n");
    }
    I compiled both with gcc -S and got the following:
    testptr.s:
    Code:
            .file   "testptr.c"
            .def    ___main;        .scl    2;      .type   32;     .endef
            .text
    .globl _main
            .def    _main;  .scl    2;      .type   32;     .endef
    _main:
            pushl   %ebp
            movl    %esp, %ebp
            subl    $8, %esp
            andl    $-16, %esp
            movl    $0, %eax
            movl    %eax, -8(%ebp)
            movl    -8(%ebp), %eax
            call    __alloca
            call    ___main
            movl    $0, -4(%ebp)
            movl    $_hello_world, -4(%ebp)
            movl    -4(%ebp), %eax
            call    *%eax
            movl    $0, %eax
            leave
            ret
            .section .rdata,"dr"
    LC0:
            .ascii "Hello, World!\12\0"
            .text
    .globl _hello_world
            .def    _hello_world;   .scl    2;      .type   32;     .endef
    _hello_world:
            pushl   %ebp
            movl    %esp, %ebp
            subl    $8, %esp
            movl    $LC0, (%esp)
            call    _printf
            leave
            ret
            .def    _printf;        .scl    2;      .type   32;     .endef
            .def    _hello_world;   .scl    2;      .type   32;     .endef
    
    testcall.s:

    Code:
            .file   "testcall.c"
            .def    ___main;        .scl    2;      .type   32;     .endef
            .text
    .globl _main
            .def    _main;  .scl    2;      .type   32;     .endef
    _main:
            pushl   %ebp
            movl    %esp, %ebp
            subl    $8, %esp
            andl    $-16, %esp
            movl    $0, %eax
            movl    %eax, -4(%ebp)
            movl    -4(%ebp), %eax
            call    __alloca
            call    ___main
            call    _hello_world
            movl    $0, %eax
            leave
            ret
            .section .rdata,"dr"
    LC0:
            .ascii "Hello, World!\12\0"
            .text
    .globl _hello_world
            .def    _hello_world;   .scl    2;      .type   32;     .endef
    _hello_world:
            pushl   %ebp
            movl    %esp, %ebp
            subl    $8, %esp
            movl    $LC0, (%esp)
            call    _printf
            leave
            ret
            .def    _printf;        .scl    2;      .type   32;     .endef
            .def    _hello_world;   .scl    2;      .type   32;     .endef
    The difference is pretty trivial:
    diff testcall.s testptr.s
    Code:
    1c1
    <       .file   "testcall.c"
    ---
    >       .file   "testptr.c"
    12,13c12,13
    <       movl    %eax, -4(%ebp)
    <       movl    -4(%ebp), %eax
    ---
    >       movl    %eax, -8(%ebp)
    >       movl    -8(%ebp), %eax
    16c16,19
    <       call    _hello_world
    ---
    >       movl    $0, -4(%ebp)
    >       movl    $_hello_world, -4(%ebp)
    >       movl    -4(%ebp), %eax
    >       call    *%eax
    
    Only the last block of the diff represents the real difference.

    These lines are setting up the pointer:
    Code:
    movl $0, -4(%ebp) //Initializes the function pointer to null
    movl $_hello_world, -4(%ebp) //Sets function pointer to the address of the hello_world routine
    These lines are actually making the call:
    Code:
    movl -4(%ebp), %eax //Move the function pointer into %eax to make the call
    call *%eax //Call the code starting at the address in %eax
    The assignment is done however many times you set up the pointer, but if you only set it once, the real difference is the complexity of the invocation. As you can see, that's only one movl, which is pretty trivial to execution time in my opinion.

    -Lee
     
  16. ChrisA macrumors G4

    Joined:
    Jan 5, 2006
    Location:
    Redondo Beach, California
    #16
    Think about operating systems, kernels, device drivers and real-time software and user configurable software

    Any time you are implementing a plug-in system where user can add their own funtions your code will get a pointer to the user's function.

    What about registering a "call back". In general this is a style of programing whare you assign functions to events and then you writte a loop which looks for events and then calls the corect function. Window systems work this way but the are many, many other real time programs that need to handle events (like if a robot arm hits a limit switch or a vision system "sees" something.)

    I have an application I'm working on right now that reads in a set of specifications for a telemetry stream (from a rocket) and builds a program to process the telemetry. the program runs on a rather specialized custom computer. So my program is a kind of compilter. The "code generator" is table driven. when it sees a certain kind of data in the spec the table causes a function to be called to handle that kind of data. I could have used a switch but I wanted a table so I could use (say) binary search, switches are always linerar. Also the table remembers "state" (what do now depends on what was done on the last cycle) and there is more then one step where one table entry can point to another entry.

    Function pointers in a table allow the logic to be changed at run-time via some kind of user interface wereas a big switch stament is configured at compile time. If the user is to be allowed to change the program's configuration. Or if it (as in my case) the program reads a "config file" at start up then the use of funtion pointers is required.
     
  17. Cromulent thread starter macrumors 603

    Cromulent

    Joined:
    Oct 2, 2006
    Location:
    The Land of Hope and Glory
    #17
    I would have thought the speed advantage would have been more noticable when calling functions recursively. I was under the impression that a function that takes arguments that are passed by value will copy the arguments into memory again where as a function pointer will point to the memory location of the already copied arguments etc. Am I wrong in that assumption?

    Good points. Thanks for the help everyone.

    So basically they allow C to become a more dynamic language meaning that you can delay certain program decisions until runtime when you have the data available?
     
  18. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #18
    Unless I have a fundamental misunderstanding (which is possible), making a direct function call vs. calling via a function pointer has no effect on the means by which parameters are passed. Passing a parameter via a pointer/reference instead of by value can make a difference for the reason you mentioned. If a pointer is smaller than your data type, at least. On x86-64 pointers are 48-bit, so char, int, float and their unsigned varieties are smaller than a pointer. Since the bus on that architecture is 64-bit, though, it's probably not a real difference for base types. When you have a large data structure that's when you will see a real difference.

    -Lee

    p.s. I think that the 48-bit pointer assertion was wrong. I think sizeof(void*) on x86-64 will be 8 bytes. I think one of the addressing modes is 48-bit, but that doesn't translate to how pointers are stored.
     
  19. Sander macrumors 6502

    Joined:
    Apr 24, 2008
    #19
    Just a minor remark: heapsort and mergesort are not part of the Standard C Library.
     
  20. yeroen macrumors 6502a

    yeroen

    Joined:
    Mar 8, 2007
    Location:
    Cambridge, MA
    #20
    Well OK, "standard" as far as the implementation found on BSD/Darwin/OSX, but they are not universal. Headers for them may or may not be found in stdlib.h on Linux or Solaris.

    BSD/Darwin/OSX also implements two versions of radix sort as part of the standard C library.
     
  21. Sander macrumors 6502

    Joined:
    Apr 24, 2008
    #21
    It's probably glibc, right..? There are quite a few (nice) things in there which aren't part of C99 (or C89, for that matter).
     

Share This Page