C function pointers and UB

Discussion in 'C Programming' started by nroberts, Aug 23, 2011.

  1. nroberts

    nroberts Guest

    I'm from C++ and having to write some C. Could someone tell me if the
    following would illicit UB? I have a feeling that it will work OK but
    I hate generating UB without knowing it.

    #include <stdlib.h>

    typedef struct object {} Object;

    Object * createObject(void) { return
    (Object*)malloc(sizeof(Object)); }
    void freeObject(Object* obj) { free(obj); }

    typedef void (*deleter)(void*);

    int main()
    {
    Object * obj = createObject();
    delete del = (deleter)&freeObject;

    del(obj);

    return 0;
    }
    nroberts, Aug 23, 2011
    #1
    1. Advertising

  2. On Aug 23, 6:48 pm, nroberts <> wrote:
    > I'm from C++ and having to write some C.  Could someone tell me if the
    > following would illicit UB?  I have a feeling that it will work OK but
    > I hate generating UB without knowing it.
    >[...]
    > void freeObject(Object* obj) { free(obj); }
    >[...]
    > typedef void (*deleter)(void*);
    >[...]
    >   delete del = (deleter)&freeObject;


    Typo: deleter del

    No, that isn't valid. It cannot work on the (admittedly rare but
    valid) systems where Object* and void* are represented differently
    (say, sizeof(void*) > sizeof(Object*)), it cannot work on the (equally
    rare but valid) systems where parameters of type Object* are passed to
    functions differently from void* (say, passed in a different
    register), and so it cannot be allowed in standard C. And because it
    is not allowed in standard C, even on systems where it could work,
    compilers may and do perform optimisations that are only valid if you
    do not cast function pointers to a different type.
    Harald van Dijk, Aug 23, 2011
    #2
    1. Advertising

  3. nroberts

    James Kuyper Guest

    On 08/23/2011 12:48 PM, nroberts wrote:
    > I'm from C++ and having to write some C. Could someone tell me if the
    > following would illicit UB? I have a feeling that it will work OK but


    Your feeling is wrong; your worries are well justified. It won't compile
    without a diagnostic message. If it does compile, it's behavior is not
    defined by the C standard, for two different reasons.

    > I hate generating UB without knowing it.
    >
    > #include <stdlib.h>
    >
    > typedef struct object {} Object;


    This is a syntax error. The struct-declaration-list in the declaration
    of a struct type is not allowed to be empty (and there is absolutely
    nothing useful you could do with such at type even if it weren't a
    syntax error).

    An implementation of C is required to issue at least one diagnostic
    message when it processes a translation unit containing a syntax error.
    It is not required to accept such a program. If it chooses to accept it
    anyway, the behavior of the resulting program is undefined, by reason of
    the lack of a definition in the standard.

    You could change it to
    typedef struct object Object;

    But then struct object would be an incomplete type, and the sizeof()
    expression down below would be a constraint violation, so that change
    wouldn't help you any.

    > Object * createObject(void) { return
    > (Object*)malloc(sizeof(Object)); }
    > void freeObject(Object* obj) { free(obj); }
    > typedef void (*deleter)(void*);
    >
    > int main()
    > {
    > Object * obj = createObject();


    I hope that, in real code, you would check whether or not 'obj' is null?
    There's no problem in this case, because the only thing you try to do
    with obj if free() it. However, if it were in fact null, just about
    anything else you might want to do with it would have undefined behavior.

    > delete del = (deleter)&freeObject;


    You've provide no definition for the identifier 'delete' in this code. I
    assume it's a typo for 'deleter'?

    The '&' is unnecessary. A name of a function is always implicitly
    converted to a pointer to the function. There's a special rule that '&'
    applied to a function pointer value always returns an equivalent pointer
    of the same type. Therefore, freeObject, &freeObject, and &&freeObject
    are all equivalent pointer values.

    C allows you to explicitly convert a pointer to a function to a pointer
    to any other function type. However, the converted pointer cannot be
    used to call that function unless the target function type is compatible
    with the source function type. Object* is not compatible with void*, so
    the function types are not compatible either. Therefore, the following
    statement:

    > del(obj);


    has undefined behavior. In practice, this might actually work - on many
    systems Object* and void* might have identical representations. However,
    they're not required to do so.

    This rule is not specific to C; C++ has a similar rule - you should not
    be doing something like this in either language. In C, this is
    completely unnecessary, because unlike C++, it allows implicit
    conversion of Object* to void*. Note: the fact that type A is implicitly
    convertible to type B does NOT mean that type A is compatible with type B.

    > return 0;
    > }
    James Kuyper, Aug 23, 2011
    #3
  4. nroberts

    nroberts Guest

    On Aug 23, 9:59 am, Harald van Dijk <> wrote:
    > On Aug 23, 6:48 pm, nroberts <> wrote:
    >
    > > I'm from C++ and having to write some C.  Could someone tell me ifthe
    > > following would illicit UB?  I have a feeling that it will work OKbut
    > > I hate generating UB without knowing it.
    > >[...]
    > > void freeObject(Object* obj) { free(obj); }
    > >[...]
    > > typedef void (*deleter)(void*);
    > >[...]
    > >   delete del = (deleter)&freeObject;

    >
    > Typo: deleter del
    >
    > No, that isn't valid. It cannot work on the (admittedly rare but
    > valid) systems where Object* and void* are represented differently
    > (say, sizeof(void*) > sizeof(Object*)), it cannot work on the (equally
    > rare but valid) systems where parameters of type Object* are passed to
    > functions differently from void* (say, passed in a different
    > register), and so it cannot be allowed in standard C. And because it
    > is not allowed in standard C, even on systems where it could work,
    > compilers may and do perform optimisations that are only valid if you
    > do not cast function pointers to a different type.


    Thanks. It's UB in C++ too.

    And thanks for not replying to email. Part of being stuck with google
    groups is not being able to post with a bounceback and usenet newbs
    inundate my email with their replies.
    nroberts, Aug 23, 2011
    #4
  5. James Kuyper <> writes:
    > On 08/23/2011 12:48 PM, nroberts wrote:

    [...]
    >> typedef struct object {} Object;

    >
    > This is a syntax error. The struct-declaration-list in the declaration
    > of a struct type is not allowed to be empty


    True.

    > (and there is absolutely
    > nothing useful you could do with such at type even if it weren't a
    > syntax error).


    Not necessarily true. There are languages that support the equivalent
    of empty structs, and they can be useful in some circumstances.

    [...]

    >> delete del = (deleter)&freeObject;

    >
    > You've provide no definition for the identifier 'delete' in this code. I
    > assume it's a typo for 'deleter'?
    >
    > The '&' is unnecessary. A name of a function is always implicitly
    > converted to a pointer to the function. There's a special rule that '&'
    > applied to a function pointer value always returns an equivalent pointer
    > of the same type. Therefore, freeObject, &freeObject, and &&freeObject
    > are all equivalent pointer values.


    freeObject and &freeObject are equivalent. &&freeObject, due to
    the maximal munch rule, is an incorrect use of the "&&" operator.
    Fixing that, &(&freeObject) is a constraint violation because
    (&freeObject) is not an lvalue. Perhaps you were thinking of
    *freeObject and **freeObject (the latter works because there's no
    "**" operator).

    [...]

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Nokia
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Aug 23, 2011
    #5
  6. nroberts

    James Kuyper Guest

    On 08/23/2011 02:57 PM, Keith Thompson wrote:
    > James Kuyper <> writes:
    >> On 08/23/2011 12:48 PM, nroberts wrote:

    > [...]
    >>> typedef struct object {} Object;

    >>
    >> This is a syntax error. The struct-declaration-list in the declaration
    >> of a struct type is not allowed to be empty

    >
    > True.
    >
    >> (and there is absolutely
    >> nothing useful you could do with such at type even if it weren't a
    >> syntax error).

    >
    > Not necessarily true. There are languages that support the equivalent
    > of empty structs, and they can be useful in some circumstances.


    Can you provide an example? I know languages where everything is derived
    from a single "Object" base class, and which allow creation of an empty
    object to which attributes can be later attached. But that involves a
    more complicated object model than C has, so I wouldn't consider an
    empty Object to be comparable to an empty struct.

    >> The '&' is unnecessary. A name of a function is always implicitly
    >> converted to a pointer to the function. There's a special rule that '&'
    >> applied to a function pointer value always returns an equivalent pointer
    >> of the same type. Therefore, freeObject, &freeObject, and &&freeObject
    >> are all equivalent pointer values.

    >
    > freeObject and &freeObject are equivalent. &&freeObject, due to
    > the maximal munch rule, is an incorrect use of the "&&" operator.
    > Fixing that, &(&freeObject) is a constraint violation because
    > (&freeObject) is not an lvalue. Perhaps you were thinking of
    > *freeObject and **freeObject (the latter works because there's no
    > "**" operator).


    You're right. My mistake.
    James Kuyper, Aug 23, 2011
    #6
  7. James Kuyper <> writes:
    > On 08/23/2011 02:57 PM, Keith Thompson wrote:
    >> James Kuyper <> writes:
    >>> On 08/23/2011 12:48 PM, nroberts wrote:

    >> [...]
    >>>> typedef struct object {} Object;
    >>>
    >>> This is a syntax error. The struct-declaration-list in the declaration
    >>> of a struct type is not allowed to be empty

    >>
    >> True.
    >>
    >>> (and there is absolutely
    >>> nothing useful you could do with such at type even if it weren't a
    >>> syntax error).

    >>
    >> Not necessarily true. There are languages that support the equivalent
    >> of empty structs, and they can be useful in some circumstances.

    >
    > Can you provide an example? I know languages where everything is derived
    > from a single "Object" base class, and which allow creation of an empty
    > object to which attributes can be later attached. But that involves a
    > more complicated object model than C has, so I wouldn't consider an
    > empty Object to be comparable to an empty struct.


    I was afraid you might ask that. :cool:}

    I suppose it gives you a way of having multiple unique objects (with
    unique addresses *if* the language guarantees that) minimal overhead.

    Ada even has a specific syntax for this:
    type Empty is null record;
    but that's mostly used when it's going to be expanded later.

    [...]

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Nokia
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Aug 23, 2011
    #7
  8. nroberts

    Ian Collins Guest

    On 08/24/11 07:19 AM, James Kuyper wrote:
    > On 08/23/2011 02:57 PM, Keith Thompson wrote:
    >> James Kuyper<> writes:
    >>> On 08/23/2011 12:48 PM, nroberts wrote:

    >> [...]
    >>>> typedef struct object {} Object;
    >>>
    >>> This is a syntax error. The struct-declaration-list in the declaration
    >>> of a struct type is not allowed to be empty

    >>
    >> True.
    >>
    >>> (and there is absolutely
    >>> nothing useful you could do with such at type even if it weren't a
    >>> syntax error).

    >>
    >> Not necessarily true. There are languages that support the equivalent
    >> of empty structs, and they can be useful in some circumstances.

    >
    > Can you provide an example? I know languages where everything is derived
    > from a single "Object" base class, and which allow creation of an empty
    > object to which attributes can be later attached. But that involves a
    > more complicated object model than C has, so I wouldn't consider an
    > empty Object to be comparable to an empty struct.


    C++.

    An empty struct can be used to create a unique type with templates.
    Another, more common, use is to hold typedefs which are used as traits
    in class templates. For example given a class that manipulates
    character data, the character type (say char or wchar_t) can be specified:

    template <typename Traits>
    struct DoSomething
    {
    typename Traits::char_type c;

    // do stuff with c.
    };

    struct Normal { typedef char char_type; };
    struct Wide { typedef wchar_t char_type; };

    int main ()
    {
    DoSomething<Normal> d;
    return 0;
    }

    --
    Ian Collins
    Ian Collins, Aug 23, 2011
    #8
  9. nroberts

    James Kuyper Guest

    On 08/23/2011 04:12 PM, Ian Collins wrote:
    > On 08/24/11 07:19 AM, James Kuyper wrote:
    >> On 08/23/2011 02:57 PM, Keith Thompson wrote:
    >>> James Kuyper<> writes:
    >>>> On 08/23/2011 12:48 PM, nroberts wrote:
    >>> [...]
    >>>>> typedef struct object {} Object;
    >>>>
    >>>> This is a syntax error. The struct-declaration-list in the declaration
    >>>> of a struct type is not allowed to be empty
    >>>
    >>> True.
    >>>
    >>>> (and there is absolutely
    >>>> nothing useful you could do with such at type even if it weren't a
    >>>> syntax error).
    >>>
    >>> Not necessarily true. There are languages that support the equivalent
    >>> of empty structs, and they can be useful in some circumstances.

    >>
    >> Can you provide an example? I know languages where everything is derived
    >> from a single "Object" base class, and which allow creation of an empty
    >> object to which attributes can be later attached. But that involves a
    >> more complicated object model than C has, so I wouldn't consider an
    >> empty Object to be comparable to an empty struct.

    >
    > C++.
    >
    > An empty struct can be used to create a unique type with templates.
    > Another, more common, use is to hold typedefs which are used as traits
    > in class templates.


    I actually knew about those uses, but forgot about them. Since C lacks
    templates and overloading, such uses don't apply in C. However, they
    might be useful in connection with the new _Generic() feature proposed
    for the next version of the C standard.
    James Kuyper, Aug 23, 2011
    #9
  10. Keith Thompson wrote:
    > James Kuyper <> writes:
    >> On 08/23/2011 02:57 PM, Keith Thompson wrote:
    >>> James Kuyper <> writes:
    >>>> On 08/23/2011 12:48 PM, nroberts wrote:
    >>> [...]
    >>>>> typedef struct object {} Object;
    >>>> This is a syntax error. The struct-declaration-list in the declaration
    >>>> of a struct type is not allowed to be empty
    >>> True.
    >>>
    >>>> (and there is absolutely
    >>>> nothing useful you could do with such at type even if it weren't a
    >>>> syntax error).
    >>> Not necessarily true. There are languages that support the equivalent
    >>> of empty structs, and they can be useful in some circumstances.

    >> Can you provide an example? I know languages where everything is derived
    >> from a single "Object" base class, and which allow creation of an empty
    >> object to which attributes can be later attached. But that involves a
    >> more complicated object model than C has, so I wouldn't consider an
    >> empty Object to be comparable to an empty struct.

    >
    > I was afraid you might ask that. :cool:}
    >
    > I suppose it gives you a way of having multiple unique objects (with
    > unique addresses *if* the language guarantees that) minimal overhead.
    >
    > Ada even has a specific syntax for this:
    > type Empty is null record;
    > but that's mostly used when it's going to be expanded later.


    In some Ada designs, a general, reusable framework makes use of various
    application-specific types, defined in application modules. Values of
    these types are created and processed in application-specific code but
    stored and passed along by the framework code. In a simple application,
    some of these types may be logically unnecessary, in which case the null
    record type is appropriate: it satisfies the framework's compile-time
    need for a type, while avoiding the existence of any dummy, unused
    actual data at run time.

    My applications have several cases of such null record types.

    I'm not sure if different objects of a null record type are required to
    have different addresses in Ada. I have not needed that.

    --
    Niklas Holsti
    Tidorum Ltd
    niklas holsti tidorum fi
    . @ .
    Niklas Holsti, Aug 23, 2011
    #10
  11. Niklas Holsti <> writes:
    [...]
    > I'm not sure if different objects of a null record type are required to
    > have different addresses in Ada. I have not needed that.


    <OT>
    I don't think that's required. Experimentally, two declared null record
    objects have distinct addresses, but array elements have the same
    address. (Note that Ada doesn't define array indexing in terms of
    addresses.)
    </OT>

    --
    Keith Thompson (The_Other_Keith) <http://www.ghoti.net/~kst>
    Nokia
    "We must do something. This is something. Therefore, we must do this."
    -- Antony Jay and Jonathan Lynn, "Yes Minister"
    Keith Thompson, Aug 23, 2011
    #11
  12. James Kuyper <> writes:
    <snip>
    > [...] In C, this is
    > completely unnecessary, because unlike C++, it allows implicit
    > conversion of Object* to void*.


    C++ allows that implicit conversion too -- it's the reverse that is not
    done implicitly.

    <snip>
    --
    Ben.
    Ben Bacarisse, Aug 23, 2011
    #12
  13. nroberts

    James Kuyper Guest

    On 08/23/2011 06:02 PM, Ben Bacarisse wrote:
    > James Kuyper <> writes:
    > <snip>
    >> [...] In C, this is
    >> completely unnecessary, because unlike C++, it allows implicit
    >> conversion of Object* to void*.

    >
    > C++ allows that implicit conversion too -- it's the reverse that is not
    > done implicitly.


    You're right - which makes this whole "deleter" typedef even more puzzling.
    --
    James Kuyper
    James Kuyper, Aug 24, 2011
    #13
  14. nroberts

    Eric Sosman Guest

    On 8/23/2011 12:48 PM, nroberts wrote:
    > I'm from C++ and having to write some C. Could someone tell me if the
    > following would illicit UB? [...]


    Others have commented on the specifics of your question, and
    even on some of its far-fetched generalities, but no one (as far
    as I've seen) has yet mentioned the other issue: "Illicit" doesn't
    mean what you think it means. It's illegal. Try "elicit."

    --
    Eric Sosman
    d
    Eric Sosman, Aug 24, 2011
    #14
  15. nroberts

    Nobody Guest

    On Tue, 23 Aug 2011 09:48:39 -0700, nroberts wrote:

    > I'm from C++ and having to write some C. Could someone tell me if the
    > following would illicit UB? I have a feeling that it will work OK but
    > I hate generating UB without knowing it.


    It would work on "common" platforms, but it's not guaranteed by the
    standard.

    For compatibility, you would need to either change the type of freeObject
    to take a void*:

    void freeObject(void* obj) { free(obj); }

    or wrap it with a function that does:

    void objectDeleter(Object* obj) { freeObject(obj); }

    BTW: providing a dedicated "free" function is a wise move even if it
    simply calls free(), as it ensures that the *correct* free() is used.

    This is a very real issue on Windows, as a process can end up using
    multiple distinct versions of MSVCRT, e.g. due to the program linking
    against one version and also against a DLL which is linked against a
    different version.

    Anything allocated by one version's malloc() must be freed using the same
    version's free(). To ensure that this happens, a library which returns
    pointers to dynamically allocated memory must provide its own deallocation
    routine(s). If the main program simply calls free(), it may well end up
    calling the wrong free().
    Nobody, Aug 24, 2011
    #15
    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. Phil
    Replies:
    1
    Views:
    639
    llewelly
    Sep 16, 2003
  2. Roger Leigh
    Replies:
    8
    Views:
    415
    Karl Heinz Buchegger
    Nov 17, 2003
  3. Peter Goddard

    void pointers & void function pointers

    Peter Goddard, May 16, 2005, in forum: C Programming
    Replies:
    3
    Views:
    505
    Peter Goddard
    May 16, 2005
  4. n2xssvv g02gfr12930

    Smart pointers and member function pointers

    n2xssvv g02gfr12930, Nov 26, 2005, in forum: C++
    Replies:
    3
    Views:
    461
    n2xssvv g02gfr12930
    Nov 27, 2005
  5. cerr

    pointers, pointers, pointers...

    cerr, Apr 7, 2011, in forum: C Programming
    Replies:
    12
    Views:
    652
Loading...

Share This Page