The C standard has two kinds of not specified behavior, the first one is implementation specific behavior, where the C standard basically does not prescribe something directly but says the implementations have to decide on it, document it and behave consistently on it, and undefined behavior, which means relying on it is not a valid C program.
One example for an implementation specific feature is the bitsize of int types. int is defined as covering at least the range of -2^16-2^16, so on one platform you can have a 16 bit 2nd complement integer, on another a 32 bit sign and magnitude and on a third platform a 47 bit integer type that uses 2 sign bits and a 16 bit crc in the end. But the same compiler on the same platform should always have the same type with the same rules, independent of optimization level or context where it is used in.
Pointer casting on the other hand is undefined except in cases where you cast to and from an integer of sufficient size, to char * (but not back), or to and from void*. Note that only casting is allowed but accessing not, so casting to an integer modifying the integer and casting back is again UB.
This means pointer casting like Qakes famous Fast Inverse Square Root is not valid standard C.
That said, not everything that is UB is an error, it only means it's not valid standard C. Compilers like MSVC, GCC or Clang sometimes provide the ability to specify UB behavior, like with -fno-strict-aliasing for this rule. But when you utilize UB, you must be in the clear that you are writing non standard C code and should usually have a good reason for doing so.
PS: There are also a few ways to access memory under a different layout that are not UB. C allows the use of unions to access a shared prefix, but only a shared prefix. So the following would be allowed:
union {
struct {int i; float f} b1;
struct {int i; char c} b2;
} u;
u.b1.i=42;
But the following not:
union {
struct {short s; float f} b1;
struct {int i; char c} b2;
} u;
u.b1.s=42;
Even though under the assumption both short and int are 2nd complement integers with sizeof(short)<sizeof(int) (which is not necessarily true) on a little endian system it should work, it's UB.
C++ on the other hand has dynamic_cast for class hierachies, and since c++20 bit_cast, which specifically fills that gap.