That's a nonsensical statement, a language cannot warn you, only a compiler can (-Wcast-align). The compiler can also decide what is and isn't an invalid pointer, this way the language avoids leaky abstractions.
It's obvious at the langauge level if you are creating, for example, an int pointer that is not guaranteed to be int-aligned. The only way to guarantee that an int pointer is int-aligned is if it comes from malloc* or new (C++), or if you take the address of an integer variable.
Any time you need to use a type cast to assign an int pointer, other than casting the output of malloc, then you potentially have a unaligned pointer, although only the compiler, knowing the alignment requirements of the target, would know if that is creating an invalid pointer or not.
* The C/C++ language (standard library) spec says that malloc must return memory that is aligned to meet the (target) requirements of all built in types, which is obviously a bit wasteful, as well not helpful for things like SIMD types that may be supported by libraries rather than built-in.
Thanks for the clarification. Yes, that's right – that's the job of the compiler/parser/etc.
I like the C programming language. To be honest, I regret not having the knowledge (low skills) to write my own compiler, or at least a standard library. I have some rough sketches on paper of what it should look like, but I can't implement it.
Different hardware targets obviously differ in many ways, not just alignment requirements, but things like endianness, integer representation, etc. C23 has finally said that signed integers must be in 2's complement representation, but this is only practically possible since all modern CPUs have also done that. Maybe endianness will be be mandated at some point since modern processors have also rallied around little endian ordering.
However, C has since day one chosen to define a leaky abstraction by putting efficiency above all else and having the language/implementation adapt to the hardware rather than vice versa (having the implementation hide hardware differences to implement some language-defined standard). The most obvious case of this is the size of the integer types short, int, and long, where the standard only says that "short <= int <= long" and specifies minimum value ranges that each type must be able to hold. The spec basically says that ints must be at least 16-bit, while on most targets they are in fact going to be 32-bit. Of course you can use int32_t etc for better portability, but nonetheless the language provides this "int" type whose size and endianness are unspecified.
C's unions could also be considered as providing a leaky abstraction, since it exposes differences between implementations - endianness, alignment/packing.
Then you get to things like size of address space, pointer sizes and representations, even amount of memory that a program may be able to allocate.
The only way a language could totally abstract away the underling hardware would be to base it on some lowest common denominator of hardware (or virtual machines) that it is willing to support, which would be totally impractical. The alternative is that you just accept the leaky abstraction and say that many things are implementation defined, not language defined.