Reputation: 45
I have a back end function that returns a void pointer (void*) and I have several front end function pointers that point to it. Each front end function pointer however needs to return a different type (always a pointer though). Because each front end function pointer always has the same unique return type, I thought it would be possible to typecast the return type of each front end pointer to its respective return type.
Hopefully my code below will explain better than I have:
//Backend function definition
void* addElement(void *memBlock, size_t origSize, size_t additionalSize){
return realloc(memBlock, origSize+additionalSize);
}
//Front end function pointers
int*(*addInt)(void*, size_t, size_t)=addElement;
char*(*addChar)(void, size_t, size_t) addChar=addElement;
My question is twofold:
How is this different from just typecasting the return value every time I call the function, like below?
int *var=(int *)addElement(...);
If it is different, will it work? Having read this discussion, and this one, it seemed to come down to a question of compatibility. Since the original function is returning void* though, it should be compatible if I'm not mistaken.
Upvotes: 2
Views: 1840
Reputation: 141618
This is all undefined behaviour according to Standard C. The type of a function pointer being called must match the defined type of the function (except that ()
can match any parameter list).
The word "compatible" in the Standard has a very strict meaning that is basically "the same". Although void *
can be implicitly converted to int *
, they are not compatible types.
For the code to be defined behaviour, you could do what you are suggesting in point 1. You could wrap this up in a macro or a wrapper function.
Upvotes: 1
Reputation: 754090
The C11 standard §6.2.5 Types ¶28 says:
A pointer to
void
shall have the same representation and alignment requirements as a pointer to a character type.48) Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements. All pointers to structure types shall have the same representation and alignment requirements as each other. All pointers to union types shall have the same representation and alignment requirements as each other. Pointers to other types need not have the same representation or alignment requirements.
That means, in particular, that pointers to functions do not have to have the same representation as any pointer to object type. There are, or at least were, machines where pointers to functions were bigger than pointers to objects (and I'm not referring to small, medium, compact, large, huge models and 80x86 chips — I believe IBM AS/400, later iSeries, had pointers to functions that were bigger than any pointer to object). Also, as Ajay Brahmakshatriya points out in a comment, some ABIs (application binary interfaces) can have different calling conventions for different function types, so converting a function pointer to the 'wrong' type and then invoking it via that converted pointer may end up wreaking havoc too. This particularly applies to pointers versus floating point versus other types. Usually (but it is by no means guaranteed), functions returning pointers will return the pointer the same way regardless of the type of the pointer returned.
And §6.3.2.3 Pointers ¶7-8 says:
A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned68) for the referenced type, the behavior is undefined. Otherwise, when converted back again, the result shall compare equal to the original pointer. When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.
A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.
As you wrote the code, you'd be calling the function through the cast type, which would lead to undefined behaviour. It might work, but it might stop working and you'd have no recourse except to rewrite the code to avoid the undefined behaviour.
This means that you are not supposed to write your front-end function pointers as shown in the question. You could use simple cover functions, though, to do the job you want:
static int *addIntFunc(void *mem, size_t old_size, size_t extra_size)
{
return addElement(mem, old_size, extra_size);
}
int *(*addInt)(void *, size_t, size_t) = addIntFunc;
You probably can't usefully inline the function; a function pointer must point at a function.
Or, indeed, you could simply make use of addIntFunc
(possibly renamed to addInt
) to get the required effect, and then an inlined function might well be useful.
Upvotes: 3