It's true that C is easy to use unsafely. ... I
suspect it is not
possible to eliminate the ability to do stupid things in C without also
eliminating the ability to do some clever things in C.
Oh so true. Computer science progress seems to be all about improving on
getting the most bang for the buck (i.e. it's really engineering, which is
all about bang/buck ratios - as the saying goes, 'an engineer is someone who
can do for $.10 what any fool can do for $1').
So assembly language is maximally flexible, but very easy to screw up.
Something like C gives you 93% of the flexibility, but with only 40% of the
chances to screw up. (Numbers only approximate, after 2 seconds of
consideration.) So further language development has had to improve on that
ratio; ideally, one should decrease the latter with impacting the former, if
possible - a non-trivial problem.
And of course the optimal flexibility/screwage ratio will differ from
programmer to programmer, and even from project to project. (I once wrote a
condition handler package for C, and elected to do it in assembler - even
though others have done in it C, using longjump(). But there are very few
things where assembler seems a better choice, to me.)
Note that PDP-11 autoincrement and autodecrement exist
... the pointers are in registers.
Of course, clever programmers will make sure their code is arranged (not
warped into an non-understandable maze, mind, just... arranged properly) to
do that! :-)
But to really do that effectively, you have to be able to control which
variables are in registers, etc, etc. Modern optimizing compilers like to do
this all for you - and do an incredibly good job, most of the time, to the
point where for most programmers, the compiler does a _better_ job than they
could. So, for 97% of programmers, probably the right thing.
For those who are writing real-time code, know exactly what instructions any
piece of code will compile into, and how many memory references are involved,
not so much. (Back to different strokes for different programmers.)
(Yes, yes, I know those optimizations often aren't portable - I wrote a fair
amount of code that ran on several architectures at the same point in time.
But what was optimal on one was usually pretty good on the others, too.)