James Kuyper
8/23/2011 5:28:00 PM
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;
> }