Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it legal to cast a function returning an object pointer to a function returning a void pointer?

These sections indicate that calling a function pointer with a not compatible type results in undefined behavior.

C89 3.5.4.3 p9

For two function types to be compatible, both shall specify compatible return types. Moreover, the parameter type lists, if both are present, shall agree in the number of parameters and in use of the ellipsis terminator; corresponding parameters shall have compatible types.

C89 3.5.4.1 p2

For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.

C89 3.3.4 p3

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 that has a type that is not compatible with the type of the called function, the behavior is undefined.

Is it compatible to cast from any other object pointer type to a void pointer?

C89 3.3.4 p3

It is guaranteed, however, that a pointer to an object of a given alignment may be converted to a pointer to an object of the same alignment or a less strict alignment and back again; the result shall compare equal to the original pointer. (An object that has character type has the least strict alignment.)

C89 3.1.2.5 p20

A pointer to void shall have the same representation and alignment requirements as a pointer to a character type.

C89 3.2.2.3 p1

A pointer to void may be converted to or from a pointer to any incomplete or object type. A pointer to any incomplete or object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.

Example:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int *foo(void) {
    int *data = malloc(sizeof(int));
    *data = 42;
    return data;
}

int main(int argc, char *argv[]) {
    void *(*fn_ptr)(void) = foo;
    void *raw = fn_ptr();
    int data = *(int *)raw;
    printf("%d\n", data);
}
like image 279
Jason Brown Avatar asked Dec 22 '22 20:12

Jason Brown


1 Answers

You ask if it is legal to cast a pointer to a function to another pointer to a function. It is legal to cast a pointer to a function to any other pointer to a function. But calling the function through such a pointer invokes undefined behavior. C11 6.5.2.2p9

6.5.2.2 Function calls

[...]

  1. If the function is defined with a type that is not compatible with the type (of the expression) pointed to by the expression that denotes the called function, the behavior is undefined.

Another problem is that your code does not have a cast. It has a forced assignment:

void *(*fn_ptr)(void) = foo;

This is invalid, having a constraint violation, that a C compiler must diagnose. A cast would read

void *(*fn_ptr)(void) = (void *(*)(void))foo;

Now the question is what is the behaviour of

void *(*fn_ptr)(void) = (void *(*)(void))foo;
void *raw = fn_ptr();
int data = *(int *)raw;

The behaviour of the construct is undefined as per the standard. Of course your implementation is free to provide a meaning to the expression. You should check your compiler manuals for the behaviour in this case.

There have been architectures where the representation for void * and int * would not have been compatible.


If you just change the function pointer to one returning an int * or cast back before calling, the behaviour is well-defined - the implicit conversion to void * and explicit cast to int * are both fine.

I.e.

int *(*fn_ptr)(void) = foo;
void *raw = fn_ptr();
int data = *(int *)raw;

or

void *(*fn_ptr)(void) = (void *(*)(void))foo;
void *raw = ((int *(*)(void))fn_ptr)();
int data = *(int *)raw;

Or let the function return void *:

void *foo(void) {
    int *data = malloc(sizeof(int));
    *data = 42;
    return data;
}

void *(*fn_ptr)(void) = foo;
void *raw = fn_ptr();
int data = *(int *)raw;
like image 180