It’s very refreshing that modern languages can make improvements every few months, and users actually adopt them.
I’m incredibly frustrated that in C nothing ever gets fixed. Not even a smallest thing. Even if the C standard fixes some wart, it takes literally 20 or 30 years before major projects allow it.
I’m really curious what should be fixed in C (besides some operators
having the “wrong” precedence).
I once asked an automotive supplier what language they use.
He said: “C, because it has to be compilable 20 years from now.”
So I guess “nothing ever getting fixed in C” is one of the reason C is
so popular in industry.
 That was a few years ago and I don’t remember whether he explicitly
said “compilable”. But I think it was deducible from context.
I’m not sure if this is a good place for a laundry list of my C pet peeves. And I’m afraid that if I list small ones, they’ll be easy to shoot down as unnecessary, and big ones as making it C++ all over again… but here it goes:
Safe way to detect numeric overflow. Unhandled overflows in malloc(len * size) are common and super dangerous. Signed overflow is UB. Integer promotions may make arithmetic unexpectedly signed. So you have to be super duper careful how you check for overflow, or you’ll make it worse and accidentally “prove” to the compiler that it can delete your safety checks.
malloc(len * size)
Syntax for intended case fall-through, so that compilers can reliably warn about unintended ones.
Default variables to initialized, and like D require = void for cases where uninit is actually wanted. Uninitialized data is painful to debug, and even worse is that the UB gives the optimizer a license to kill. I’ve wasted soo much time on weird bugs because of this, and it has never made a difference in performance (since the optimizer can eliminate redundant initializations anyway).
Some UB is necessary for performance (e.g. without signed overflow being UB indexing by int would be slow), but there’s also a ton of UB for 70s weirdo machines, segmented memory, non-8-bit-bytes. I’m sure a ton of it could be dropped without regret.
A standard way to silence warnings from the code itself. I like to have pedantic warnings, but sometimes I have to tell the compiler that I’ve reviewed the warning and it’s OK. In C this requires either non-standard attribute or pragma, or silly tricks to hide the offending code from the compiler, which may backfire by causing other warnings in another compiler.
Actual immutability. const is a joke. Const pointer to const data paradoxically means mutable data (due to wrong variance). const guarantees are too weak for optimizers to use them for anything.
Non-nullable pointers. ObjC has them. Syntax is of course ugly, but they’re very useful.
I could do without the ritual of include guards. It feels like absolutely necessary best practice only because include is dumb, and there’s no include_once.
If there was a mechanism like Go’s defer, it’d spare me from so so many goto cleanups or deeply nested else free. In code where I really have to care about correctness and memory leaks I feel like 80% of functions is dedicated to error handling. OTOH in Rust it’s just ? in a few places, and it’s even more reliable.
Good to see error wrapping in there.