Function pointer to void function and int function

Discussion in 'C Programming' started by Giannis Papadopoulos, Sep 5, 2005.

  1. I have the following code

    #include <stdio.h>

    void a(void) {
    printf("a called.\n");
    }

    int b(void) {
    printf("b called.\n");

    return 0x1;
    }

    int main(void) {
    void (*p)(void);

    p = a;
    p();
    p = b;
    p();

    return 0;
    }

    However, it does issue a warning that I am trying to assign a function
    of wrong type. Is there any portable way to say to the compiler "shut
    up, I know what I am doing?"

    And what is the most common way of declaring and using function
    pointers, the above or this?

    p = &a;
    (*p)();



    --
    one's freedom stops where others' begin

    Giannis Papadopoulos
    http://dop.users.uth.gr/
    University of Thessaly
    Computer & Communications Engineering dept.
     
    Giannis Papadopoulos, Sep 5, 2005
    #1
    1. Advertising

  2. Giannis Papadopoulos

    Eric Sosman Guest

    Giannis Papadopoulos wrote:

    > I have the following code
    >
    > #include <stdio.h>
    >
    > void a(void) {
    > printf("a called.\n");
    > }
    >
    > int b(void) {
    > printf("b called.\n");
    >
    > return 0x1;
    > }
    >
    > int main(void) {
    > void (*p)(void);
    >
    > p = a;
    > p();
    > p = b;
    > p();
    >
    > return 0;
    > }
    >
    > However, it does issue a warning that I am trying to assign a function
    > of wrong type. Is there any portable way to say to the compiler "shut
    > up, I know what I am doing?"


    Yes, there is:

    p = (void(*)(void))b;

    However, you'd be telling a lie, because you *don't* know what
    you're doing! The subsequent `p();' invokes undefined behavior,
    because it uses a pointer of one type to call a function of a
    different type. You wouldn't expect

    double (*q)(const char*) = (double(*)(const char*))strlen;
    double d = q("Hello, world!");

    to work, would you? Well, the situation's no different in your
    code: the pointer does not match the function, the compiler puts
    together the wrong kind of function linkage, and anything can
    happen.

    > And what is the most common way of declaring and using function
    > pointers, the above or this?
    >
    > p = &a;
    > (*p)();


    It's a matter of taste, I think. I seldom see `p = &a;',
    perhaps for the same reason that `char *s = "Hello, world!";' is
    more common than `char *s = &"Hello, world!"[0];'. As for the
    call, on some pre-Standard compilers this form was required, and
    coders who worked in those days may still use it out of habit.
    Some may continue to do so just to make it clear that the call
    is using a function pointer and not a literal function name; they
    feel the code is more "self-documenting" if written this way.
    I prefer to write `p();' instead of the more cluttered `(*p)();',
    but the preference isn't a strong one.

    --
    Eric Sosman
    lid
     
    Eric Sosman, Sep 5, 2005
    #2
    1. Advertising

  3. Eric Sosman wrote:
    > Giannis Papadopoulos wrote:
    >
    >> I have the following code
    >>
    >> #include <stdio.h>
    >>
    >> void a(void) {
    >> printf("a called.\n");
    >> }
    >>
    >> int b(void) {
    >> printf("b called.\n");
    >>
    >> return 0x1;
    >> }
    >>
    >> int main(void) {
    >> void (*p)(void);
    >> p = a;
    >> p();
    >> p = b;
    >> p();
    >>
    >> return 0;
    >> }
    >>
    >> However, it does issue a warning that I am trying to assign a function
    >> of wrong type. Is there any portable way to say to the compiler "shut
    >> up, I know what I am doing"?

    >
    >
    > Yes, there is:
    >
    > p = (void(*)(void))b;
    >
    > However, you'd be telling a lie, because you *don't* know what
    > you're doing! The subsequent `p();' invokes undefined behavior,
    > because it uses a pointer of one type to call a function of a
    > different type. You wouldn't expect
    >
    > double (*q)(const char*) = (double(*)(const char*))strlen;
    > double d = q("Hello, world!");
    >
    > to work, would you? Well, the situation's no different in your
    > code: the pointer does not match the function, the compiler puts
    > together the wrong kind of function linkage, and anything can
    > happen.


    Yes but all I want is to ignore the returning value.
    And I need a function pointer to switch between the 2.

    Hmm, maybe this would be better?

    #include <stdio.h>

    void a(void) {
    printf("a called.\n");
    }

    int b(void) {
    printf("b called.\n");
    return 0x1;
    }

    static void c(void) {
    b();
    }

    int main(void) {
    void (*p)(void);

    p = &a;
    p();
    p = &c;
    p();

    return 0;
    }

    --
    one's freedom stops where others' begin

    Giannis Papadopoulos
    http://dop.users.uth.gr/
    University of Thessaly
    Computer & Communications Engineering dept.
     
    Giannis Papadopoulos, Sep 5, 2005
    #3
  4. Giannis Papadopoulos

    Eric Sosman Guest

    Giannis Papadopoulos wrote:
    > Eric Sosman wrote:
    >> [... function pointer type must match type of called function ...]

    > Yes but all I want is to ignore the returning value.
    > And I need a function pointer to switch between the 2.


    The issue is that the type of the function's value is
    part of the "signature," and can influence the way the function
    is called and the way its returned value (if any) is retrieved.
    For example, some implementations compile

    struct foo func(void) {
    struct foo f;
    ...
    return f;
    }
    ...
    struct foo f2 = func();

    as if it had been written

    void func(struct foo* _value_ptr) {
    struct foo f;
    ...
    *_value_ptr = f;
    }
    ...
    struct foo f2;
    struct foo _returned_struct;
    foo(&_returned_struct);
    f2 = _returned_struct;

    to avoid the need to find a "secret place" (stack, register,
    whatever) that's suitable for holding a possibly large struct.
    If you call such a function through a `(void(*)(void))' pointer,
    the compiler will not know it's supposed to generate the hidden
    `_value_ptr' argument nor the hidden `_returned_struct' object
    it should point to. Even if you intend to ignore the returned
    value, it's obvious such a call is wrong and possibly fatal.

    Now, on many machines it will turn out that the machinery
    that calls a function returning `int' and ignores the returned
    value is identical to the machinery that calls a `void' function.
    This is partly accidental, but also partly intentional. In pre-
    Standard C there was no `void', so the way one wrote what we'd
    now describe as a `void' function was to write a function that
    (formally speaking) was `int'-valued, even though it would not
    actually return a value. The people who develop C compilers have
    considerable incentive to see to it that as much old code as
    possible continues to work, so there's an excellent chance that
    you'll get away with your abuse of the language. However, it
    is an abuse, and new code should avoid it. Someday, somebody
    will find he can generate more efficient code if he does not
    cater to practices that are more than fifteen years out of date,
    and then where will you be? Don't Do That.

    > Hmm, maybe this would be better?
    >
    > #include <stdio.h>
    >
    > void a(void) {
    > printf("a called.\n");
    > }
    >
    > int b(void) {
    > printf("b called.\n");
    > return 0x1;
    > }
    >
    > static void c(void) {
    > b();
    > }
    >
    > int main(void) {
    > void (*p)(void);
    >
    > p = &a;
    > p();
    > p = &c;
    > p();
    >
    > return 0;
    > }


    This is fine. In fact, this is an excellent technique
    for using pointers to call functions that may have widely
    different argument and result types: You "wrap" each target
    function in a "helper" function, and see to it that all the
    helpers have the same type.

    --
    Eric Sosman
    lid
     
    Eric Sosman, Sep 5, 2005
    #4
  5. On Mon, 05 Sep 2005 16:37:23 +0300, Giannis Papadopoulos
    <> wrote:

    >Eric Sosman wrote:
    >> Giannis Papadopoulos wrote:
    >>
    >>> I have the following code
    >>>
    >>> #include <stdio.h>
    >>>
    >>> void a(void) {
    >>> printf("a called.\n");
    >>> }
    >>>
    >>> int b(void) {
    >>> printf("b called.\n");
    >>>
    >>> return 0x1;
    >>> }
    >>>
    >>> int main(void) {
    >>> void (*p)(void);
    >>> p = a;
    >>> p();
    >>> p = b;
    >>> p();
    >>>
    >>> return 0;
    >>> }
    >>>
    >>> However, it does issue a warning that I am trying to assign a function
    >>> of wrong type. Is there any portable way to say to the compiler "shut
    >>> up, I know what I am doing"?

    >>
    >>
    >> Yes, there is:
    >>
    >> p = (void(*)(void))b;
    >>
    >> However, you'd be telling a lie, because you *don't* know what
    >> you're doing! The subsequent `p();' invokes undefined behavior,
    >> because it uses a pointer of one type to call a function of a
    >> different type. You wouldn't expect
    >>
    >> double (*q)(const char*) = (double(*)(const char*))strlen;
    >> double d = q("Hello, world!");
    >>
    >> to work, would you? Well, the situation's no different in your
    >> code: the pointer does not match the function, the compiler puts
    >> together the wrong kind of function linkage, and anything can
    >> happen.

    >
    >Yes but all I want is to ignore the returning value.


    But you can't. The compiler knows the code for any function called
    via p returns nothing. Therefore, it can make assumptions about what
    happens when such a function returns. For example, if int returns are
    always done in register x, then register x will be unchanged. If
    register x is used by code before and after the function call, then it
    may not be necessary to reload its value.

    In your case, after p=b this will no longer be true. b() changed the
    value of register x but the compiler doesn't know it. It might
    attempt to reuse the value of register x but now it will get the
    residue from b().

    >And I need a function pointer to switch between the 2.


    There are other techniques that don't lead to undefined behavior, such
    as a wrapper function, if-then, changing a() so it always returns 0,
    etc.

    >
    >Hmm, maybe this would be better?
    >
    >#include <stdio.h>
    >
    >void a(void) {
    > printf("a called.\n");
    >}
    >
    >int b(void) {
    > printf("b called.\n");
    > return 0x1;
    >}
    >
    >static void c(void) {
    > b();
    >}
    >
    >int main(void) {
    > void (*p)(void);
    >
    > p = &a;


    The & is superfluous.

    > p();
    > p = &c;
    > p();
    >
    > return 0;
    >}

    In the sense that it does not lead to undefined behavior, yes, it is
    better.


    <<Remove the del for email>>
     
    Barry Schwarz, Sep 5, 2005
    #5
  6. On Mon, 05 Sep 2005 16:37:23 +0300, Giannis Papadopoulos
    <> wrote:

    >Eric Sosman wrote:
    >> Giannis Papadopoulos wrote:
    >>
    >>> I have the following code
    >>>
    >>> #include <stdio.h>
    >>>
    >>> void a(void) {
    >>> printf("a called.\n");
    >>> }
    >>>
    >>> int b(void) {
    >>> printf("b called.\n");
    >>>
    >>> return 0x1;
    >>> }
    >>>
    >>> int main(void) {
    >>> void (*p)(void);
    >>> p = a;
    >>> p();
    >>> p = b;
    >>> p();
    >>>
    >>> return 0;
    >>> }
    >>>
    >>> However, it does issue a warning that I am trying to assign a function
    >>> of wrong type. Is there any portable way to say to the compiler "shut
    >>> up, I know what I am doing"?

    >>
    >>
    >> Yes, there is:
    >>
    >> p = (void(*)(void))b;
    >>
    >> However, you'd be telling a lie, because you *don't* know what
    >> you're doing! The subsequent `p();' invokes undefined behavior,
    >> because it uses a pointer of one type to call a function of a
    >> different type. You wouldn't expect
    >>
    >> double (*q)(const char*) = (double(*)(const char*))strlen;
    >> double d = q("Hello, world!");
    >>
    >> to work, would you? Well, the situation's no different in your
    >> code: the pointer does not match the function, the compiler puts
    >> together the wrong kind of function linkage, and anything can
    >> happen.

    >
    >Yes but all I want is to ignore the returning value.


    But you can't. The compiler knows the code for any function called
    via p returns nothing. Therefore, it can make assumptions about what
    happens when such a function returns. For example, if int returns are
    always done in register x, then register x will be unchanged. If
    register x is used by code before and after the function call, then it
    may not be necessary to reload its value.

    In your case, after p=b this will no longer be true. b() changed the
    value of register x but the compiler doesn't know it. It might
    attempt to reuse the value of register x but now it will get the
    residue from b().

    >And I need a function pointer to switch between the 2.


    There are other techniques that don't lead to undefined behavior, such
    as a wrapper function, if-then, changing a() so it always returns 0,
    etc.

    >
    >Hmm, maybe this would be better?
    >
    >#include <stdio.h>
    >
    >void a(void) {
    > printf("a called.\n");
    >}
    >
    >int b(void) {
    > printf("b called.\n");
    > return 0x1;
    >}
    >
    >static void c(void) {
    > b();
    >}
    >
    >int main(void) {
    > void (*p)(void);
    >
    > p = &a;


    The & is superfluous.

    > p();
    > p = &c;
    > p();
    >
    > return 0;
    >}

    In the sense that it does not lead to undefined behavior, yes, it is
    better.


    <<Remove the del for email>>
     
    Barry Schwarz, Sep 5, 2005
    #6
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Ollej Reemt
    Replies:
    7
    Views:
    566
    Jack Klein
    Apr 22, 2005
  2. Schnoffos
    Replies:
    2
    Views:
    1,235
    Martien Verbruggen
    Jun 27, 2003
  3. Stig Brautaset

    `void **' revisited: void *pop(void **root)

    Stig Brautaset, Oct 25, 2003, in forum: C Programming
    Replies:
    15
    Views:
    812
    The Real OS/2 Guy
    Oct 28, 2003
  4. Replies:
    5
    Views:
    850
    S.Tobias
    Jul 22, 2005
  5. Replies:
    1
    Views:
    417
    Victor Bazarov
    May 23, 2007
Loading...

Share This Page