>>18C makes no guarantees about the way the implementation allocates memory. It is true that memory layout is predictable in many C implentations, but this is not guaranteed by the language itself. C code that makes assumptions about memory layout is very much non-portable and implentation specific.
Prior to C11 (and continuing today, since most C users of note do not rely on C11 support) the situation with multi-threading was very similar; all behavior was implementation specific. In theory, other standards like POSIX solved this problem at least for particular platforms but you don't need to look very far to find supposedly POSIX conforming compilers that break badly under the right conditions with multithreading (*cough* gcc *cough*)
The as-if rule for ordering is a joke too; it does not even come close to capturing the semantics needed for any code that does more than just manipulate values in application memory. Once you introduce external interaction of any kind, be it MMIO, debugger run control, OS traps... it all falls down and you are left with implentation defined behavior again.