There is indeed a big difference between "undefined" behavior and "implementation-defined" behavior.
For example, there's a lot of spooky "undefined behavior" around dereferencing pointers. In one famous case [1], dereferencing a pointer actually led the compiler to skip a later check on whether the pointer was NULL, because if the pointer was already dereferenced then it must have been valid.
Another classic "implementation-defined" detail is what is the size of a "char"? Nowadays we can readily assume it's 8 bits, but that wasn't so guaranteed when C was written!