This isn't a crazy thought to have about K&R C. They're trying to fit a high level language onto a 1970s computer and so sacrifices must be made. Some of the trades they make are... questionable and others I'd say clearly wrong (they just don't need Tony's billion dollar mistake, nor to be so cavalier with types in general), but it's not as though they're targeting a machine with gigabytes of RAM and a multi-core CPU.
But, people aren't writing K&R C. These days most of them are writing something closer to C99 or C11 and some may be using something newer (e.g. C17) or proprietary (GNU's C dialect) either deliberately or just because it compiled and nobody told them not to.
At that point you've actually given away much of the simplicity, and yet what you get for your trouble is largely more footguns. Trade up for a sound type system and fewer footguns instead.
ISO C comes with a list of constructs that the language is entitled to silently miscompile. Varying from "those bytes can't be an integer, I'll delete that function for you" through "signed integers can't overflow, so that overflow check you wrote must return false, so I'll delete it for you".
This makes simple application code slightly faster. That's kind of a reasonable domain for C++ but on dubious ground for C. Where C is superb is talking to hardware and language implementation, exactly the areas the ISO group has chosen to cripple the language for.
Thankfully compilers know about this and provide fno-strict-aliasing et al. Maybe there should be a std= style flag to change to the language subset that doesn't actively try to miscompile your program chasing benchmark numbers.
What things simplicity do you lose? I think there isn't much of a difference between these dialects. Most important one might be possibility to define variables on the spot instead of at top of block. Then, some people like C99 compound literals. I don't think any of these break simplicity, they are quality-of-life improvements with no interactions with the rest of the language semantics.
Next one is what, the C11 memory model? Doesn't take away any simplicity, just defines things better.
Here's my thinking. It's fair to say C99 isn't that much more complicated than C89, which formalizes various things that are a bad idea such as "volatile", as well as numerous good ideas like hey we should let you define a variable where you use the variable - however C99 adds more of the bad like "restrict".
In both those cases the K&R C model was very simple. You could decide you love how simple this model is, and when smarter compilers optimise it into a pretzel or other languages are faster that's OK. This code used to drive the serial port, now it does nothing, OK, don't use C to write such drivers. This code used to go real fast, now everybody else is faster, OK, don't use C if you need the best performance.
C89 and C99 choose different, making the language more complicated to keep addressing performance and compatibility. In C99 I can write the fast serial port driver, but it's significantly more complicated as a result. The beginner will definitely get it wrong and explaining why is pretty subtle.
Then C11 says actually you're not writing sequential programs, which was a crucial simplification in K&R C - the programs you can write do one thing at a time, in order, and then maybe stop. The memory model in C11 is needed because it says actually your programs might do more or different things at once.
Now, in reality by 2011 lots of people were writing C that's not actually sequential - after all SMP Linux long pre-dates C11. But those weren't legal C programs, they're GNU's dialect and so all bets are off. Nobody is claiming Linux is simple.
So C11 definitely isn't the simple language for a 1970s computer any more. C11 is a competitor with C++ or today Rust. And it doesn't fare so well by that comparison.
So you're focusing on the memory model. To which I'll say, it's not the language but it's the _machines_ that have SMP so effects will be visible out of order. The machines provide you with escape hooks to serialize read and write effects, and the language must give you access to these hooks because if the language opts to keep up the illusion of all sequential effects even in the face of SMP, it has to do so by serializing basically all memory accesses, which is going to be incredibly slow.
That code doesn't appear to be sequential from other threads is not the language's fault. It's just a fact of reality.
If you don't want to do SMP / lock-free programming, you don't have to pay for the complexity. You can still write "sequential programs" like it's K&R. Don't do multiple threads, and you don't have to care. You can also do multiple threads but use mutexes for synchronisation, everything will be fine.
Pretty much nobody uses restrict or volatile. There is some fringe stuff that is obsolete and maybe badly designed, but by and large you don't have to use it or interact with it. Compare this to other languages / ecosystems which have this amount of cruft times 100.
It's still this language where you can define a struct, then define a function that receives a pointer to the struct, and it does as you say, and you can actually read and understand the code a month later.
If you need to go fancy for a reason or another (most likely a bad reason), you can do that too, and of course you'll have to pay for it. But most likely, the complexity is not "cancerous" -- define a simple function interface with the most basic assumptions you require, and the complexity is pretty much isolated behind a simple pointer to an opaque structure.
> So C11 definitely isn't the simple language for a 1970s computer any more. C11 is a competitor with C++ or today Rust. And it doesn't fare so well by that comparison.
"Faster but wrong" isn't really faster it's just wrong.
> That code doesn't appear to be sequential from other threads is not the language's fault. It's just a fact of reality.
What other threads? In a sequential programming language we certainly can't have "other threads".
> You can still write "sequential programs" like it's K&R.
"You can just not use the bits you don't like" is how we got C++ dialects that C++ proponents love to pretend don't exist. If the language is "simple" except for all the new bits that are complicated it's not simple.
With all 3 of these quotes and replies I feel completely misunderstood. I'm not at all trying to say the things that you're contradicting with. Won't engage anymore, but maybe you can reevaluate.
K&R gets used as a shorthand for the "portable assembly" C that some people remember and others swear never existed. That's my guess at what the parent meant, not the syntactic strangeness of writing types after the parameter list.
C11 atomics would have been much better if they'd standardised existing practice instead of inventing something based around the application centric design C++ was inventing. It's nice that there's an acquire/release scheme one can reason with but a real shame that they conflated it with type annotations.
But, people aren't writing K&R C. These days most of them are writing something closer to C99 or C11 and some may be using something newer (e.g. C17) or proprietary (GNU's C dialect) either deliberately or just because it compiled and nobody told them not to.
At that point you've actually given away much of the simplicity, and yet what you get for your trouble is largely more footguns. Trade up for a sound type system and fewer footguns instead.