Thread: Help with double use of arrow operator with pointer, function. "pointer->pointer->" ?

  1. #1
    Registered User
    Join Date
    Feb 2015
    Posts
    7

    Help with double use of arrow operator with pointer, function. "pointer->pointer->" ?

    Hello to the forum. I have recently come across a function call that I do not understand. It uses the arrow operator for a function call, twice, and I don't understand.

    Code:
    static inline void b43_write32(struct b43_wldev *dev, u16 offset, u32 value)
    {
            dev->dev->write32(dev->dev, offset, value);
    }

    I see that the function itself does not return anything but calls another function. The main difficulty I have is with the "dev->dev->" operator, where dev, I expect is a pointer to a structure.

    Help is appreciated.
    Thank You
    Tom

  2. #2
    Programming Wraith GReaper's Avatar
    Join Date
    Apr 2009
    Location
    Greece
    Posts
    2,738
    "dev" is obviously a struct pointer
    "dev->dev" is another struct's pointer
    "dev->dev->write32" is a function pointer, which can be used the same way as a "regular" function.
    Devoted my life to programming...

  3. #3
    Registered User
    Join Date
    Feb 2015
    Posts
    7
    Quote Originally Posted by GReaper View Post
    "dev" is obviously a struct pointer
    "dev->dev" is another struct's pointer
    "dev->dev->write32" is a function pointer, which can be used the same way as a "regular" function.
    Greaper, thank you for the response.

    What confuses me is that dev->dev-> is using the "same" structure pointer twice. How is that possible? I would expect dev->dev2-> to be clear enough. Could you elaborate somewhat further?

    Thanks again.
    Tom

  4. #4
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Consider this:
    Code:
    struct ops {
        ssize_t (*write32)(struct ops *, u16, u32);
        /* Here is probably some other device-specific state */
    };
    
    struct device {
        struct ops *dev;
    };
    
    struct device *dev;
    Can you now see what

    Code:
    dev->dev->write32(dev->dev, offset, value)
    does?

    The first dev is the variable name; the second dev is the name of the member in the structure, and the write32 is the name of the function pointer within the structure.

    The first argument to the function is the pointer to the structure we used to obtain the function pointer from. It is common, since it is the logical place to put any device-specific state, and there is no automagic way in C to obtain that.

  5. #5
    Registered User
    Join Date
    Feb 2015
    Posts
    7
    Quote Originally Posted by Nominal Animal View Post
    Consider this:
    Code:
    struct ops {
        ssize_t (*write32)(struct ops *, u16, u32);
        /* Here is probably some other device-specific state */
    };
    
    struct device {
        struct ops *dev;
    };
    
    struct device *dev;
    Can you now see what

    Code:
    dev->dev->write32(dev->dev, offset, value)
    does?

    The first dev is the variable name; the second dev is the name of the member in the structure, and the write32 is the name of the function pointer within the structure.

    The first argument to the function is the pointer to the structure we used to obtain the function pointer from. It is common, since it is the logical place to put any device-specific state, and there is no automagic way in C to obtain that.


    Thank You, Nominal Animal, for the most informative example. There is only one part that I do not understand which relates to the first argument to the pointer function. Please check out my understanding of this.

    1) In the above, "struct ops" is a structure definition that holds a function, write32, which returns a type ssize_t and accepts a pointer of type "struct ops" as well as two other values. The structure "ops" could also have other instructions.

    2) "struct device" is a structure that only contains a pointer to type "struct ops", and it is called dev.

    3) "struct device" is then instantiated.

    4) The first dev, the variable name, points to the struct "device".

    5) The second dev, points to the struct "ops", while "write32" is the function that is selected.

    I fail to understand the "dev->dev->", which is what the function "write32" accepts as its first argument. in
    Code:
    dev->dev->write32(dev->dev, offset, value)
    In the original structure definition
    Code:
    ssize_t (*write32)(struct ops *, u16, u32);)
    the function accepts a pointer to the structure of type struct ops and I am not sure of why this is necessary. You wrote "The first argument to the function is the pointer to the structure we used to obtain the function pointer from".
    That sounds somewhat circular to me. the structure is already pointed to by "dev->dev->write32.


    Thank you again.
    Tom

  6. #6
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by miner_tom View Post
    In the original structure definition
    Code:
    ssize_t (*write32)(struct ops *, u16, u32);
    the function accepts a pointer to the structure of type struct ops and I am not sure of why this is necessary.
    Because a function has no way of knowing how it was called in C.

    It only knows that it was called, and the parameters it was called with. (The function can also access any variables visible in the scope it was implemented in, but that won't help here: via function pointers, you can call the function from scopes that are utterly unreachable from the scope the function was implemented in.)

    In particular, the function called has no way of knowing whether it was called directly (write32(...)) or via a function pointer (dev->dev->write32(...)).

    Let's look at a complete example you can compile and run:
    Code:
    #include <stdlib.h>
    #include <stdio.h>
    
    struct inner {
        int (*kick)(int);
        int (*punt)(struct inner *, int);
        int   id;
    };
    
    struct outer {
        struct inner *ptr;
    };
    
    static int kick1(int x)
    {
        return x;
    }
    
    static int kick2(int x)
    {
        return x*x;
    }
    
    static int punt(struct inner *ptr, int x)
    {
        return ptr->id + x;
    }
    
    int main(void)
    {
        struct outer *var_a, *var_b;
    
        /* Initialize var_a. */
        var_a = malloc(sizeof *var_a);
        if (!var_a) {
            fprintf(stderr, "Out of memory.\n");
            return EXIT_FAILURE;
        }
        var_a->ptr = malloc(sizeof *(var_a->ptr));
        if (!var_a->ptr) {
            fprintf(stderr, "Out of memory.\n");
            return EXIT_FAILURE;
        }
        var_a->ptr->id = 1;
        var_a->ptr->kick = kick1;
        var_a->ptr->punt = punt;
    
        /* Initialize var_b. */
        var_b = malloc(sizeof *var_b);
        if (!var_b) {
            fprintf(stderr, "Out of memory.\n");
            return EXIT_FAILURE;
        }
        var_b->ptr = malloc(sizeof *(var_b->ptr));
        if (!var_b->ptr) {
            fprintf(stderr, "Out of memory.\n");
            return EXIT_FAILURE;
        }
        var_b->ptr->id = 2;
        var_b->ptr->kick = kick2;
        var_b->ptr->punt = punt;
    
        printf("var_a = (struct outer *)0x%llx\n", (unsigned long long)var_a);
        printf("var_a->ptr = (struct inner *)0x%llx\n", (unsigned long long)(var_a->ptr));
        printf("var_a->ptr->kick = function pointer to 0x%llx\n", (unsigned long long)(void (*)(void))(var_a->ptr->kick));
        printf("var_a->ptr->punt = function pointer to 0x%llx\n", (unsigned long long)(void (*)(void))(var_a->ptr->punt));
        printf("var_a->ptr->id = %d\n", var_a->ptr->id);
        printf("\n");
        printf("var_b = (struct outer *)0x%llx\n", (unsigned long long)var_b);
        printf("var_b->ptr = (struct inner *)0x%llx\n", (unsigned long long)(var_b->ptr));
        printf("var_b->ptr->kick = function pointer to 0x%llx\n", (unsigned long long)(void (*)(void))(var_b->ptr->kick));
        printf("var_b->ptr->punt = function pointer to 0x%llx\n", (unsigned long long)(void (*)(void))(var_b->ptr->punt));
        printf("var_b->ptr->id = %d\n", var_b->ptr->id);
        printf("\n");
        printf("var_a->ptr->kick(3) returns %d.\n", var_a->ptr->kick(3));
        printf("var_a->ptr->punt(var_a->ptr, 3) returns %d.\n", var_a->ptr->punt(var_a->ptr, 3));
        printf("\n");
        printf("var_b->ptr->kick(3) returns %d.\n", var_b->ptr->kick(3));
        printf("var_b->ptr->punt(var_b->ptr, 3) returns %d.\n", var_b->ptr->punt(var_b->ptr, 3));
        printf("\n");
        printf("kick1(3) returns %d\n", kick1(3));
        printf("kick2(3) returns %d\n", kick2(3));
        printf("\n");
        printf("var_a->ptr->punt(var_a->ptr, 6) returns %d.\n", var_a->ptr->punt(var_a->ptr, 6));
        printf("var_b->ptr->punt(var_b->ptr, 6) returns %d.\n", var_b->ptr->punt(var_b->ptr, 6));
        printf("var_a->ptr->punt(var_b->ptr, 6) returns %d.\n", var_b->ptr->punt(var_a->ptr, 6));
        printf("var_b->ptr->punt(var_a->ptr, 6) returns %d.\n", var_a->ptr->punt(var_b->ptr, 6));
    
        return EXIT_SUCCESS;
    }
    Compiled an run, it outputs something like
    Code:
    var_a = (struct outer *)0x21bd010
    var_a->ptr = (struct inner *)0x21bd030
    var_a->ptr->kick = function pointer to 0x40064d
    var_a->ptr->punt = function pointer to 0x400669
    var_a->ptr->id = 1
    
    var_b = (struct outer *)0x21bd050
    var_b->ptr = (struct inner *)0x21bd070
    var_b->ptr->kick = function pointer to 0x400659
    var_b->ptr->punt = function pointer to 0x400669
    var_b->ptr->id = 2
    
    var_a->ptr->kick(3) returns 3.
    var_a->ptr->punt(var_a->ptr, 3) returns 4.
    
    var_b->ptr->kick(3) returns 9.
    var_b->ptr->punt(var_b->ptr, 3) returns 5.
    
    kick1(3) returns 3
    kick2(3) returns 9
    
    var_a->ptr->punt(var_a->ptr, 6) returns 7.
    var_b->ptr->punt(var_b->ptr, 6) returns 8.
    var_a->ptr->punt(var_b->ptr, 6) returns 7.
    var_b->ptr->punt(var_a->ptr, 6) returns 8.
    If you examine the code, you can see that in any kick function, you do not have access to the id member.

    To get access to the id member, you need to supply a pointer to the struct inner structure to the function, like I do with the punt functions.

    Above, there is only one punt() function implemented; both var_a->ptr->punt and var_b->ptr->punt point to the same function (which happened to be at address 0x400669).

    Think of this as the same punt() being used in two different games, and its behaviour varies depending on the game it is used in. (The game being analogous to the struct inner structure here.)
    However, kick1() and kick2() differ (maybe your foot is positioned differently between the two), but have no reference to any game, no access to any ids, so they always behave the same way.

    Look closely at the last two lines of the output, and you'll notice that I deliberately provided the wrong pointer to the punt() function.
    [I]It does not matter which pointer I used to reach the punt function; it will use the id member from its first parameter.

    If you omit the first parameter to the punt functions, you get the exact same interface as the kick functions have. However, the kick functions do not have any access to the id member. Indeed, the example even calls kick1() and kick2() directly, which should be proof enough that there is no relevant id at all.

  7. #7
    Registered User
    Join Date
    Feb 2015
    Posts
    7
    Quote Originally Posted by Nominal Animal View Post
    Because a function has no way of knowing how it was called in C.

    It only knows that it was called, and the parameters it was called with. (The function can also access any variables visible in the scope it was implemented in, but that won't help here: via function pointers, you can call the function from scopes that are utterly unreachable from the scope the function was implemented in.)

    In particular, the function called has no way of knowing whether it was called directly (write32(...)) or via a function pointer (dev->dev->write32(...)).

    Let's look at a complete example you can compile and run:
    Code:
    #include <stdlib.h>
    #include <stdio.h>
    
    struct inner {
        int (*kick)(int);
        int (*punt)(struct inner *, int);
        int   id;
    };
    
    struct outer {
        struct inner *ptr;
    };
    
    static int kick1(int x)
    {
        return x;
    }
    
    static int kick2(int x)
    {
        return x*x;
    }
    
    static int punt(struct inner *ptr, int x)
    {
        return ptr->id + x;
    }
    
    int main(void)
    {
        struct outer *var_a, *var_b;
    
        /* Initialize var_a. */
        var_a = malloc(sizeof *var_a);
        if (!var_a) {
            fprintf(stderr, "Out of memory.\n");
            return EXIT_FAILURE;
        }
        var_a->ptr = malloc(sizeof *(var_a->ptr));
        if (!var_a->ptr) {
            fprintf(stderr, "Out of memory.\n");
            return EXIT_FAILURE;
        }
        var_a->ptr->id = 1;
        var_a->ptr->kick = kick1;
        var_a->ptr->punt = punt;
    
        /* Initialize var_b. */
        var_b = malloc(sizeof *var_b);
        if (!var_b) {
            fprintf(stderr, "Out of memory.\n");
            return EXIT_FAILURE;
        }
        var_b->ptr = malloc(sizeof *(var_b->ptr));
        if (!var_b->ptr) {
            fprintf(stderr, "Out of memory.\n");
            return EXIT_FAILURE;
        }
        var_b->ptr->id = 2;
        var_b->ptr->kick = kick2;
        var_b->ptr->punt = punt;
    
        printf("var_a = (struct outer *)0x%llx\n", (unsigned long long)var_a);
        printf("var_a->ptr = (struct inner *)0x%llx\n", (unsigned long long)(var_a->ptr));
        printf("var_a->ptr->kick = function pointer to 0x%llx\n", (unsigned long long)(void (*)(void))(var_a->ptr->kick));
        printf("var_a->ptr->punt = function pointer to 0x%llx\n", (unsigned long long)(void (*)(void))(var_a->ptr->punt));
        printf("var_a->ptr->id = %d\n", var_a->ptr->id);
        printf("\n");
        printf("var_b = (struct outer *)0x%llx\n", (unsigned long long)var_b);
        printf("var_b->ptr = (struct inner *)0x%llx\n", (unsigned long long)(var_b->ptr));
        printf("var_b->ptr->kick = function pointer to 0x%llx\n", (unsigned long long)(void (*)(void))(var_b->ptr->kick));
        printf("var_b->ptr->punt = function pointer to 0x%llx\n", (unsigned long long)(void (*)(void))(var_b->ptr->punt));
        printf("var_b->ptr->id = %d\n", var_b->ptr->id);
        printf("\n");
        printf("var_a->ptr->kick(3) returns %d.\n", var_a->ptr->kick(3));
        printf("var_a->ptr->punt(var_a->ptr, 3) returns %d.\n", var_a->ptr->punt(var_a->ptr, 3));
        printf("\n");
        printf("var_b->ptr->kick(3) returns %d.\n", var_b->ptr->kick(3));
        printf("var_b->ptr->punt(var_b->ptr, 3) returns %d.\n", var_b->ptr->punt(var_b->ptr, 3));
        printf("\n");
        printf("kick1(3) returns %d\n", kick1(3));
        printf("kick2(3) returns %d\n", kick2(3));
        printf("\n");
        printf("var_a->ptr->punt(var_a->ptr, 6) returns %d.\n", var_a->ptr->punt(var_a->ptr, 6));
        printf("var_b->ptr->punt(var_b->ptr, 6) returns %d.\n", var_b->ptr->punt(var_b->ptr, 6));
        printf("var_a->ptr->punt(var_b->ptr, 6) returns %d.\n", var_b->ptr->punt(var_a->ptr, 6));
        printf("var_b->ptr->punt(var_a->ptr, 6) returns %d.\n", var_a->ptr->punt(var_b->ptr, 6));
    
        return EXIT_SUCCESS;
    }
    Compiled an run, it outputs something like
    Code:
    var_a = (struct outer *)0x21bd010
    var_a->ptr = (struct inner *)0x21bd030
    var_a->ptr->kick = function pointer to 0x40064d
    var_a->ptr->punt = function pointer to 0x400669
    var_a->ptr->id = 1
    
    var_b = (struct outer *)0x21bd050
    var_b->ptr = (struct inner *)0x21bd070
    var_b->ptr->kick = function pointer to 0x400659
    var_b->ptr->punt = function pointer to 0x400669
    var_b->ptr->id = 2
    
    var_a->ptr->kick(3) returns 3.
    var_a->ptr->punt(var_a->ptr, 3) returns 4.
    
    var_b->ptr->kick(3) returns 9.
    var_b->ptr->punt(var_b->ptr, 3) returns 5.
    
    kick1(3) returns 3
    kick2(3) returns 9
    
    var_a->ptr->punt(var_a->ptr, 6) returns 7.
    var_b->ptr->punt(var_b->ptr, 6) returns 8.
    var_a->ptr->punt(var_b->ptr, 6) returns 7.
    var_b->ptr->punt(var_a->ptr, 6) returns 8.
    If you examine the code, you can see that in any kick function, you do not have access to the id member.

    To get access to the id member, you need to supply a pointer to the struct inner structure to the function, like I do with the punt functions.

    Above, there is only one punt() function implemented; both var_a->ptr->punt and var_b->ptr->punt point to the same function (which happened to be at address 0x400669).

    Think of this as the same punt() being used in two different games, and its behaviour varies depending on the game it is used in. (The game being analogous to the struct inner structure here.)
    However, kick1() and kick2() differ (maybe your foot is positioned differently between the two), but have no reference to any game, no access to any ids, so they always behave the same way.

    Look closely at the last two lines of the output, and you'll notice that I deliberately provided the wrong pointer to the punt() function.
    [I]It does not matter which pointer I used to reach the punt function; it will use the id member from its first parameter.

    If you omit the first parameter to the punt functions, you get the exact same interface as the kick functions have. However, the kick functions do not have any access to the id member. Indeed, the example even calls kick1() and kick2() directly, which should be proof enough that there is no relevant id at all.

    OMG Nominal Animal, you are amazing. What a clear explanation of how pointer functions perform within structures!

    Thank You
    Tom

  8. #8
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by miner_tom View Post
    OMG Nominal Animal, you are amazing. What a clear explanation of how pointer functions perform within structures!
    I am unable to tell whether that was sarcasm, or intended at face value.

    If intended as a compliment: thanks, glad I could help.

    If it was sarcasm: I did notice I forgot the two important cases from the example,
    Code:
        printf("punt(var_a->ptr, 3) returns %d.\n", punt(var_a->ptr, 3));
        printf("punt(var_b->ptr, 3) returns %d.\n", punt(var_b->ptr, 3));
    that show that you can call the function that takes the pointer to the inner structure as the first parameter directly, too. Definitely, the example could be better, but it was the best I could do in a single quick sitting.
    These things get better with criticism and feedback and teamwork, which is why I prefer to try to help here in a discussion forum, rather than write my own guide or programming book. (That is, sarcasm is wasted here, but criticism and enhancement suggestions always welcome.)

    One very interesting thing about function pointers one rarely realizes, is that you can use the pointer to call a function whose definition is not in scope. That is, you may not have any way to call the original function, because it was defined elsewhere, say, as local (static) to another object file, or something like that. This property makes it extremely useful in things like run-time plug-in mechanisms: you only need one public function in the plug-in object, which returns or populates an array or structure with the functions it supports. As long as the array or structure is well defined -- almost always the same for all plugins of the same type -- there is no risk of namespace collisions and so on.

  9. #9
    Registered User
    Join Date
    Feb 2015
    Posts
    7
    Quote Originally Posted by Nominal Animal View Post
    I am unable to tell whether that was sarcasm, or intended at face value.

    If intended as a compliment: thanks, glad I could help.

    If it was sarcasm: I did notice I forgot the two important cases from the example,
    Code:
        printf("punt(var_a->ptr, 3) returns %d.\n", punt(var_a->ptr, 3));
        printf("punt(var_b->ptr, 3) returns %d.\n", punt(var_b->ptr, 3));
    that show that you can call the function that takes the pointer to the inner structure as the first parameter directly, too. Definitely, the example could be better, but it was the best I could do in a single quick sitting.
    These things get better with criticism and feedback and teamwork, which is why I prefer to try to help here in a discussion forum, rather than write my own guide or programming book. (That is, sarcasm is wasted here, but criticism and enhancement suggestions always welcome.)

    One very interesting thing about function pointers one rarely realizes, is that you can use the pointer to call a function whose definition is not in scope. That is, you may not have any way to call the original function, because it was defined elsewhere, say, as local (static) to another object file, or something like that. This property makes it extremely useful in things like run-time plug-in mechanisms: you only need one public function in the plug-in object, which returns or populates an array or structure with the functions it supports. As long as the array or structure is well defined -- almost always the same for all plugins of the same type -- there is no risk of namespace collisions and so on.
    Hi Nominal Animal.

    My reply, complimenting you on this most helpful and clearly outlined post, was in no way intended as sarcasm. It was intended as the most sincere complement. I mean it. Your post was the best explanation concerning pointer functions that I probably will ever see.

    Best regards and thanks.
    Tom Cipollone

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 14
    Last Post: 11-08-2010, 01:47 AM
  2. what is wrong with my function? my pointer is failed :"(
    By roadorange in forum C Programming
    Replies: 3
    Last Post: 01-10-2010, 01:05 AM
  3. Replies: 3
    Last Post: 10-30-2009, 04:41 PM
  4. Replies: 2
    Last Post: 02-27-2009, 12:46 PM
  5. Replies: 7
    Last Post: 10-31-2002, 08:01 AM