One issue is that the memory model isn't just a flat space that can be addresses by any pointer value - it may look similar to one if your compiler and OS let you, but doing things like accessing memory allocated as a different type or outside (an array of) objects is invalid, and the compiler is perfectly allowed by the standard to assume that never happens and happily "optimize" everything that may be a result of that away.
A lot of bugs have been caused by programmers assuming any access to the 'linear address space' is fine, but that has never been reliable as it's not allowed by the standard. The worse thing is when it looks like it works for a while, but you're relying on stuff not allowed by the standard so may change at any time (like a compiler version or option change, or even a change to a different part of the code that happens to tickle the compiler's analysis stages a slightly different way). See the "Time traveling NULL-check removal" - as the compiler "knows" that no pointer can ever have the value of NULL during deference, any path that does that can be completely removed - even if there's something like a NULL check and a logging output before said deference, if compiler decides that deference will eventually happen in that path unconditionally, that path and logging before the deference Can Never Happen so can be removed.
Or type punning and pointer aliasing - objects are created with a type, and so the compiler Knows if you convert a pointer type to another type that isn't compatible with the first type, they somehow magically point to different memory, and all the assumptions that implies for the following code.
A lot of these restrictions are pretty similar to things like Java have - the difference is that the JVM checks and flags violations and/or straight up disallows them when compiling - not just allowing the compiler to (silently) optimize based on those assumptions, and throwing the result at hardware to see what happens.
There may be a few platform/compiler-specific behavior used to implement super low-level stuff like OSs, but that's platform-specific stuff outside the C++ (or C) spec itself.
A lot of bugs have been caused by programmers assuming any access to the 'linear address space' is fine, but that has never been reliable as it's not allowed by the standard. The worse thing is when it looks like it works for a while, but you're relying on stuff not allowed by the standard so may change at any time (like a compiler version or option change, or even a change to a different part of the code that happens to tickle the compiler's analysis stages a slightly different way). See the "Time traveling NULL-check removal" - as the compiler "knows" that no pointer can ever have the value of NULL during deference, any path that does that can be completely removed - even if there's something like a NULL check and a logging output before said deference, if compiler decides that deference will eventually happen in that path unconditionally, that path and logging before the deference Can Never Happen so can be removed.
Or type punning and pointer aliasing - objects are created with a type, and so the compiler Knows if you convert a pointer type to another type that isn't compatible with the first type, they somehow magically point to different memory, and all the assumptions that implies for the following code.
A lot of these restrictions are pretty similar to things like Java have - the difference is that the JVM checks and flags violations and/or straight up disallows them when compiling - not just allowing the compiler to (silently) optimize based on those assumptions, and throwing the result at hardware to see what happens.
There may be a few platform/compiler-specific behavior used to implement super low-level stuff like OSs, but that's platform-specific stuff outside the C++ (or C) spec itself.