C doesn’t lack safety by-design - it’s hobbled by its history and constraints imposed by its userbase - otherwise C would have major breaking changes more often.
C absolutely lacks type-safety by design. Otherwise what would malloc return, besides void*? How would you implement generic containers in C, without using macros or void*?
> Otherwise what would malloc return, besides void*
IIRC, historically void* didn't exist, and it used char*. It added void as a unit type, and absent historical baggage could add... let's call it "noreturn" as a bottom type:
#define NULL ((noreturn*)0)
noreturn* malloc(size_t);
void free(void*);
noreturn exit(int); // never returns
int main() {
// where (T)... is any expression of type T
void x = (T)...; // throw away the value
T y = (noreturn)...; // a nonterminating expression
void* p = (T*)...; // pointers convertible *to* void*
T* q = (noreturn*)...; // pointers convertible *from* noreturn*
*(noreturn*)...; // notionally, this should always fault
*(void*)...; // read zero bytes, so always fine
}