There is nothing wrong with that - most people understand that C was
never meant to be a safe language.
I would go a step beyond that and say that there were definite decisions
to allow unsafe code to meet the design goals of the language.
"Safety" in a language tends to come at the cost of expressiveness,
efficiency or clerity. You lose expressiveness when the static
protections in the language keep you from doing operations because they
might be unsafe (but also might be safe). An example of this would be
trying to prevent the returning of the address of a local variable from
a function. Since it would be possible to pass the address of the
variable to another function and get the address back, you almost need
to prohibit taking the address of any local variable.
A second way to get safety is to lose efficiency by adding automatic run
time checks to detect that an error has happened. In our example, the
pointer could have an additional field attached that somehow lets us
detect if it is from a context that no longer exists. (or better, check
for being passed outside the context).
The third cost, can happen when to minimize the loss of expressiveness,
warts are added to declarations to allow the language to permit
operations that can now be seen to be "safe" because the programmer can
add assertions that certain things won't happen, so other actions are
allowed. For example, the address of a local variable could be passed to
another function, if it promises that it won't return that value or set
any globals with it (or maybe declare that it may return that value so
its return can't be used to sneak out the address).
C, from its original design, was intended to be a language as efficient
as possible, and also as expressive as possible. This leads to the
language being "unsafe". Some major pitfalls might be blocked, but only
if it really doesn't make sense, or there is a work around to do so if
need be. One example of this is casting pointers to change their type.