Can anyone explain why this is undefined behaviour? UBSan calls it "indirect call of a function through a function pointer of the wrong type"
struct foo {int i;};
int func(struct foo *x) {return x->i;}
int main() {
int (*funcptr)(void*) = (int (*)(void*)) &func;
struct foo foo = { 42 };
return funcptr(&foo);
}
While this is all kosher per the language lawyers: struct foo {int i;};
int func(void *x) {return ((struct foo *)x)->i;}
int main() {
int (*funcptr)(void*) = &func;
struct foo foo = { 42 };
return funcptr(&foo);
}It's undefined behavior due to the "strict aliasing" rule. You're simply not allowed to cast one pointer type to another (ever!) except for the following exceptions:
- casting an object pointer to or from void*
- casting an object pointer to or from char*
You're not doing either of those things. A function pointer is not an object pointer (the standard does not guarantee that the two kinds of pointer even have the same size/representation, and in fact on some esoteric hardware they don't), and even if it were, you aren't casting to or from void* or char*. So it's UB for two separate reasons.
Two function pointer (in practice) compatible or not depends on machine specific calling convention.
I guess enumerating all the possibility is just .. don't look right? make the standard too long and complex?
Casting to a pointer of incompatible type is UB. The exception is casting to char*.
[dead]
C23 §6.5.2.2p7
> 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.
Compatible types requires integrating texts from several different paragraphs, but the general notion is "identical type, in a frontend sense", not "same ABI." This means that "const void " and "void " are not compatible types, much less "void " and "struct foo ".