Rafael said:
Is multiplying by zero a ub in math?
No. Multiplying by zero is correct in math (so my analogy is not
perfect).
Here, in math, there is a single "undefined thing" : division by zero.
But, the idea is that you can't compensate UB, in any way...
Trying to compensate with well-defined behavior doesn't help (the
multiplication by zero), and trying to compensate with undefined
behavior is not better.
Jordan Abel:
The part you quoted doesn't support that. I'm not saying you're wrong,
i'm saying the quote doesn't support it. It says you can't _call_
a function through a pointer of the wrong type.
No, the standard doesn't say that.
It says that you can convert (with an explicit C cast) a function to a
pointer to function of an incompatible type, and cast it back to a
compatible type... Otherwise, using a pointer converted (with an
explicit C cast... Not any mean of buggy reintepretation of the bytes
of the representation of the pointer) from an original int(*)() pointer
to a void*(*)() pointer and calling a function on this new pointer
creates UB.
Strictly speaking, your program contains a single pointer conversion :
from int(*)() to void*(*)().
The original pointer is int(*)()... Nowhere in your code does appear an
explicit conversion from void*(*)() to int(*)().... You just have a
buggy mean to get a int(*)() pointer from nowhere sensible (from a
symbol which should refer to an int() function but that you interpret
as a void*() function).
For instance, if the compiler documents that the first cast yields a
valid pointer to function. For instance, the compiler (with an
extension) has two different "overloaded" functions : an "int
malloc(void)" and a "void* malloc(size_t)", and the compiler documents
that the explicit declaration of "int malloc()" is valid and refers to
a function which is quite different from "void* malloc()". For
instance, this "int malloc()" function could be an "atom allocator",
yielding a different int at each call (until they are released with
"void free(int)").
Such compiler extension doesn't affect any strictly conforming program
(and thus, the compiler remains ISO-compliant) and seems even quite
sensible (except that overloading malloc is not a good idea).
In that case, the declaration has not UB... But the conversion
int(*)()->void*(*)() yields a pointer that you can't use. It would be
ok, if converted back to int(*)()
Combining UB is not a good idea.