Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C Programming > C function pointers and UB

Reply
Thread Tools

C function pointers and UB

 
 
nroberts
Guest
Posts: n/a
 
      08-23-2011
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;
}
 
Reply With Quote
 
 
 
 
Harald van Dijk
Guest
Posts: n/a
 
      08-23-2011
On Aug 23, 6:48*pm, nroberts <(E-Mail Removed)> 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.
 
Reply With Quote
 
 
 
 
James Kuyper
Guest
Posts: n/a
 
      08-23-2011
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;
> }

 
Reply With Quote
 
nroberts
Guest
Posts: n/a
 
      08-23-2011
On Aug 23, 9:59*am, Harald van Dijk <(E-Mail Removed)> wrote:
> On Aug 23, 6:48*pm, nroberts <(E-Mail Removed)> 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.
 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      08-23-2011
James Kuyper <(E-Mail Removed)> 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.velocityreviews.com/forums/(E-Mail Removed) <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"
 
Reply With Quote
 
James Kuyper
Guest
Posts: n/a
 
      08-23-2011
On 08/23/2011 02:57 PM, Keith Thompson wrote:
> James Kuyper <(E-Mail Removed)> 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.
 
Reply With Quote
 
Keith Thompson
Guest
Posts: n/a
 
      08-23-2011
James Kuyper <(E-Mail Removed)> writes:
> On 08/23/2011 02:57 PM, Keith Thompson wrote:
>> James Kuyper <(E-Mail Removed)> 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. }

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) (E-Mail Removed) <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"
 
Reply With Quote
 
Ian Collins
Guest
Posts: n/a
 
      08-23-2011
On 08/24/11 07:19 AM, James Kuyper wrote:
> On 08/23/2011 02:57 PM, Keith Thompson wrote:
>> James Kuyper<(E-Mail Removed)> 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
 
Reply With Quote
 
James Kuyper
Guest
Posts: n/a
 
      08-23-2011
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<(E-Mail Removed)> 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.
 
Reply With Quote
 
Niklas Holsti
Guest
Posts: n/a
 
      08-23-2011
Keith Thompson wrote:
> James Kuyper <(E-Mail Removed)> writes:
>> On 08/23/2011 02:57 PM, Keith Thompson wrote:
>>> James Kuyper <(E-Mail Removed)> 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. }
>
> 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
. @ .
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
pointers, pointers, pointers... cerr C Programming 12 04-07-2011 11:17 PM
Smart pointers and member function pointers n2xssvv g02gfr12930 C++ 3 11-27-2005 10:51 AM
void pointers & void function pointers Peter Goddard C Programming 3 05-16-2005 09:44 PM
Pointers and References (and References to Pointers) Roger Leigh C++ 8 11-17-2003 10:14 AM
Template specialization of pointers with function pointers Phil C++ 1 09-16-2003 02:17 AM



Advertisments