[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c

C function pointers and UB

Noah Roberts

8/23/2011 4:49:00 PM

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;
}
14 Answers

Harald van D?k

8/23/2011 4:59:00 PM

0

On Aug 23, 6:48 pm, nroberts <roberts.n...@gmail.com> 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.

James Kuyper

8/23/2011 5:28:00 PM

0

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;
> }

Noah Roberts

8/23/2011 5:32:00 PM

0

On Aug 23, 9:59 am, Harald van D?k <true...@gmail.com> wrote:
> On Aug 23, 6:48 pm, nroberts <roberts.n...@gmail.com> 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.

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.

Keith Thompson

8/23/2011 6:58:00 PM

0

James Kuyper <jameskuyper@verizon.net> 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) kst-u@mib.org <http://www.ghoti.ne...
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

James Kuyper

8/23/2011 7:19:00 PM

0

On 08/23/2011 02:57 PM, Keith Thompson wrote:
> James Kuyper <jameskuyper@verizon.net> 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.

Keith Thompson

8/23/2011 7:34:00 PM

0

James Kuyper <jameskuyper@verizon.net> writes:
> On 08/23/2011 02:57 PM, Keith Thompson wrote:
>> James Kuyper <jameskuyper@verizon.net> 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. 8-)}

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) kst-u@mib.org <http://www.ghoti.ne...
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Ian Collins

8/23/2011 8:12:00 PM

0

On 08/24/11 07:19 AM, James Kuyper wrote:
> On 08/23/2011 02:57 PM, Keith Thompson wrote:
>> James Kuyper<jameskuyper@verizon.net> 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

James Kuyper

8/23/2011 8:29:00 PM

0

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<jameskuyper@verizon.net> 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.

Niklas Holsti

8/23/2011 9:01:00 PM

0

Keith Thompson wrote:
> James Kuyper <jameskuyper@verizon.net> writes:
>> On 08/23/2011 02:57 PM, Keith Thompson wrote:
>>> James Kuyper <jameskuyper@verizon.net> 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. 8-)}
>
> 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
. @ .

Keith Thompson

8/23/2011 9:59:00 PM

0

Niklas Holsti <niklas.holsti@tidorum.invalid> 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) kst-u@mib.org <http://www.ghoti.ne...
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"