C isn't an iota more fundamental than say Modula-2. It's just more popular. And C is very bad at being a low level language. There are plenty of things that you can express in assembly that you just can't do in C. And then the platform ABI won't support it, since C can't support it, and it hampers other languages that don't have C's limitations.
A couple of examples what's missing from C from a low level perspective: Multiple return values (why can't I have a function using more registers than rax or the stack to push back more values?), sane varargs, stack manipulation (an enabler for coroutines and a lot more)
Generally, in an ideal low-level language I wouldn't expect to be hampered because the language is designed for a notional abstract machine that doesn't have things like an actual stack and contiguous RAM.
Also C isn't well suited to a modern multi-core computer. The "fix" in C11 is to just say we have the C++ 11 Memory Ordering Model and here are some built-ins for the ordered atomic operations that implies.
Having any model at all is probably most of the value and only one part of the model (consume ordering) is unimplementable fantasy so it could have been much worse, but it's hardly a triumph for a "fundamental" language is it?
Rust does have the same Memory Ordering idea (minus the ill-fated "Consume" ordering) but provides richer tools for a modern world than C does. The C standard library provides nothing to actually do concurrency, such as making more threads, nor to synchronize them, such as with a mutex whereas of course Rust provides std::thread::spawn and std::sync::Mutex
In C this is a platform specific problem, for Unix systems you want pthreads for example while Windows provides a different arrangement.
Rust does not provide out-of-the-box structured concurrency - which I would like - but it's a lot closer to what I'd prefer here.
The C standard library has had <threads.h> since C11 https://www.cppreference.com/c/thread and there are now a nonzero number of systems that support it! (glibc, musl, maybe others?)
I'm definitely the wrong person to ask because my solution was to write Rust.
I think the machines C was conceived for are small enough by today's standards that I would not bring a high level language (like C) to the fight, a macro assembler and you'll be fine. For today's much larger machines, use Rust.
Oh you've got 16-bit addressing? Chart all the addresses and what you'll use them for on a white board or a big sheet of paper. You do not need "allocators" like malloc, that's Steve's job, Steve has a sharpie to "allocate" memory on the chart.
Ironically I think to make the best use of such a small memory window, you'd want a compacting garbage collector. Lots of the 16-bit era BASICs used compacting GC for their strings.
Yes you can statically allocate, but who wants an artificial limitation like "16 strings, 256 bytes each" when some users want 200 strings of 8 bytes and some users want 4 strings of 500 bytes. Static allocation is something you can do when your memory is large compared to your use cases.
I think I didn't make it very clear. I think that by fundamental, I meant that C does not do a lot of management on its own. So there is not a lot that another language could do different that adds a lot of value.
Mathematics has much less tolerance for "undefined".
> thinking it can be low level AND safe.
"low level and unsafe" is something that people will eventually get tired of once there is a big enough disaster. Or at least start slapping Prop 65 warnings on unsafe products.
What I said was not as funny as some making a throwaway just to make this lame comment...
By lame, I don't mean in a bad way. I mean, it is pretty harmless or non-offensive. I am amused by the fact that you were afraid to say it in your regular id..
C has a high level assembly language at its roots - almost every other language does not. That's what makes it fundamental. But someone else could design a different high level assembly language.
C doesn't provide an assembler at all. So if your C implementation provide an assembler that's a non-standard extension. What do you get? It depends, can you use it with a different compiler for the same target? It depends.
So probably you're instead thinking "C is like an assembly language" and that's entirely wrong. The C abstract machine is very weird. No machines exactly like that have ever existed or will ever be built, so you're not programming a real machine. The closest ones were in the 1970s, machines today are very different.
IMO the worst outcome of this "Oh C is just assembler" nonsense is the compound volatile operations.
You probably imagine a line of C like `foo |= 1` ensures `foo` has the LSB set right? You may or may not realise that "Set the LSB of a location in memory" isn't an operation most CPUs have but hey, in C it was a single operation so...
As a hack when compilers got smarter C also has a "volatile" type qualifier, so `foo` can have the type qualifier "volatile" meaning that updates to this variable will definitely happen in memory, the compiler won't just keep `foo` in a CPU register to accelerate things, it has promised to write it back to memory...
So now, `foo |= 1` looks like a single CPU operation which should set the LSB of foo. But of course your CPU probably can't actually do that, so what's really emitted by your C compiler is read foo from memory into a CPU register, set the LSB of that register and then write the whole register back to memory. Which is three distinct operations in sequence, and thus now it can be interrupted by other work and then carry on, despite the fact that meanwhile foo changed...
Oops. The C looks fine, but the machine code generated may introduce a massive bug.
If you do want a language with assembly, Rust provides that. It provides both inline assembler which you can use inside your Rust functions, and bare assembly. Of course that's not safe Rust, but presumably if you wanted to write assembly you were on board with taking the responsibility.
Rust made the mistake of thinking it can be low level AND safe. It tried to have the cake and eat it too. That will be its downfall.