Some compiler writers interpret the Standard as deprecating "non-portable or erroneous" program constructs, including constructs which implementations for commonplace hardware had to date had unanimously processed in a manner consistent with implementation-defined behavioral traits such as numeric representations.
Compilers that are designed for paying customers will look at a construct like:
unsigned long int i = *(unsigned long int*)&f; ; f is of type float
and recognize that while converting the address of a float
to an unsigned long*
is non-portable construct, it was almost certainly written for the purpose of examining the bits of a float
type. This is a very different situation from the one offered in the published Rationale as being the reason for the rule, which was more like:
int x;
int test(double *p)
{
x = 1;
*p = 2.0;
return x;
}
In the latter situation, it would be theoretically possible that *p
points to or overlaps x
, and that the programmer knows what precedes and/or follows x
in memory, and the authors of the Standard recognized that having the function unconditionally returned 1 would be incorrect behavior if that were the case, but decided that there was no need to mandate support for such dubious possibilities.
Returning to the original, that represents a completely different situation since any compiler that isn't willfully blind to such things would know that the address being accessed via type unsigned long*
was formed from a pointer of type float*
. While the Standard wouldn't forbid compilers from being willfully blind to the possibility that a float*
might actually hold the address of storage that will be accessed using type float
, that's because the Standard saw no need to mandate that compiler writers do things which anyone wanting to sell compilers would do, with or without a mandate.
Probably not coincidentally, the compilers I'm aware of that would require a -fno-strict-aliasing
option to usefully process constructs such as yours also require that flag in order to correctly process some constructs whose behavior is unambiguously specified by the Standard. Rather than jumping through hoops to accommodate a deficient compiler configurations, a better course of action would be to simply use the "don't make buggy aliasing optimizations" option.