In a better language than C, you could encode valid addresses in a proper type whose constructor would fail. But yes, you can treat everything as a string in any language.
You can also use type safety to enforce input validation in C. Just return an error code from the initializer, and annotate it must check. (Or, if you want to couple memory allocation with the checks, write a factory that returns null on input validation errors).
Various parts of the stdlib do this, though often without the compiler annotation. They often hide the struct members inside a compilation unit to prevent callers from bypassing the check (as much as is possible in a language with unsafe primitives).
C have types too. Put your string into a struct, do not expose struct fields directly but make an API to access/modify those fields and it won't be any different.
Various parts of the stdlib do this, though often without the compiler annotation. They often hide the struct members inside a compilation unit to prevent callers from bypassing the check (as much as is possible in a language with unsafe primitives).