> the vast majority could safely assume flat memory addressing, 8-bit chars, 32-bit ints, two's complement, IEEE floating point
IME I've rarely, if ever, needed to make any of those assumptions, though early in my career I unfortunately did. Unless you're writing a kernel or a compiler there's rarely a good reason to assume a flat memory model. You would assume a flat memory model if you're manipulating objects in a way that grossly subverts the typing system; I say grossly because assuming a flat memory model usually isn't necessary even for most illegal type punning hacks.
Assuming 8-bit chars is useful, yes, but really only so you don't need to insert masks everywhere when doing bitwise operations on chars, such as when marshaling integer types (preferably without type punning). AFAIU an 8-bit ASCII string won't be packed 2 characters to a char on implementations using a 16-bit char, so pointer arithmetic and string manipulation will always look the same assuming the same string encoding. As with integers more generally, most of the time all that matters is that an integer type has at least N bits, whether or not you'll use them.
If you're depending on fixed-width types then usually there's a leak in your abstraction somewhere or you're doing bitwise manipulations where it's probably wise to use explicit masking, if only to make the code more clear, if not provide the ability to parameterize the value range. Typical exceptions would be cryptographic code, hashes, etc, particularly code that needs to perform rotations. But thankfully C has provided fixed-width types for nearly 20 years now, and almost all (if not all) compilers, even niche compilers, support these.
Assuming two's complement is only useful when you're depending on overflow characteristics. But signed overflow is undefined, anyhow (and for still-good reasons), and unsigned integer types are already guaranteed to be using two's complement representation (which requires emulation on some architectures, similar to how compilers transparently synthesize 64-bit long long integers on 32-bit platforms).
I've never done heavy floating point arithmetic so can't speak to the usefulness of assuming IEEE floating point for, e.g., managing error accumulation. But IME general purpose software likes to assume IEEE floating point for its indirect benefit, like how JavaScript provides the ability to perform accurate 32-bit scalar arithmetic by dint of providing a single 64-bit IEEE floating point integer type, or how VMs (including JavaScript JIT VMs) abuse floating point to implement tagged types; but arguably the reliance on 64-bit IEEE floating point has been on balance detrimental by, e.g., making the adoption of 64-bit scalar arithmetic in languages like JavaScript more difficult.
Also, it's noteworthy that WebAssembly makes use of some of the flexibility (or, rather, stricture) of the C standard. Appreciating the subtleties of C semantics helps make your code portable not only to archaic environments, but to bleeding-edge and future environments.