it's sort of like trying to argue that "Whose Line" is not a game without points. There are types there, but they don't matter at all. You can cast anything to anything and it will never fail.
Not sure where I was reading this the other day, but types have two advantages: abstraction and checking.
You can add a third one to that list: safety.
C has types in the sense that it allows you to do abstraction, and type checking, but it doesn't enforce safety.
2/3 doesn't seem bad to me, especially for a systems language.
I'd say that Typescript is in a similar position, you can cast anything to any type you want and there is no runtime check to stop you, but it helps you add structure to your code.
Types do very much matter in these languages in practice, since you want to very much want to avoid writing a giant mess.
Of course C has safety features for the type system, warnings for problematic casts, and structs that can't be cast unless specifically unionized and explicitly ordered to.
Try to cast everything to the same type in C, and you quickly see that isn't true.
While C does not give you safety, the typing is important for ease of access to members of structures, for example, and for deciding the number of bits or bytes to operate on, and for deciding layout of a type.
The moment you go fully typeless, you suddenly need to be explicit about layout, size and signedness far more places.
That program refuses to compile not because you can't cast to/from arbitrary types in C, but because you don't make your intent explicit (assuming I read what you tried to do correctly). Try this:
int f(float x) {
return *(int *)&x;
}
On one hand you demonstrate that C cares a tiny little bit about type, but mostly because of implicit numeric conversions rather than any serious attempt at stropping you from bad casting.
I do agree with the point that C isn't "typeless" in any real sense, though.
The intent was to show that (at least one implementation of) C does stop a completely nonsensical cast from float to pointer, not to demonstrate anything semi-sensible like getting the bits of a float.
I think the original was intentional. If C refuses to let you cast from a non-pointer to a pointer, then it is in fact enforcing some (small) degree of type-safety.
The reason I assumed it was not, is that obtaining the bitwise representation of a float is something people actually often ask for (and my variation is not the "right" way to do so). You may be right
C does not refuse to let you cast from a non-pointer to a pointer either, though. It just forces you to be slightly more devious in this case:
return *(int **)&x;
Though I think the implicit conversions that force some of these contortions (e.g. "(int *)(int)x" compiles, but does not return an integer pointer with the address equal to the bit pattern of "x" interpreted as an integer, but the integer portion of the float reinterpreted as a pointer) are more convincing demonstration that C is not typeless in a meaningful way in my opinion, as they also mean identical statements will give different results depending on the types of the variables.