Really liked the trick of defining the struct in the return part of the function.
Array pointers: Array to pointer decay is extremely annoying, if it was implemented as Array to "slice" decay it would be great.
Static array indices in function parameter declarations: awesome, a shame that C++ (and Tiny C) do not support it >/
flexible array member: extremely useful, and now there are good compiler flags for ensuring correct flexible array member usage
X-Macro: nice, no-overhead enum to string name. Didn't know the trick
Combining default, named and positional arguments: Named-arguments/default-arg, C version xD. It would be cool if it was added to C language as a native feature, instead of having to do the struct hiding macro.
Comma operator: really useful, specially in macros
Digraphs, trigraphs and alternative tokens: di/tri/graphs rarely useful, alternatives synonims of iso646.h are awesome, love using and/or instead of &&/||
Designated initializer: super awesome, could not use if you wanted C++ portability. Now C++ supports some part of it.
Compound literals: fantastic, but in C++ it will explode due to stack deallocation in the same line. C++ should fix this and allow the C idiom >/
Bit fields: nice for more control of structs layout
constant string concat: "MultiLine" String, C version xD
Ad hoc struct declaration in the return type of a function: didn't know this trick, "multi value" return, C version xD
Cosmopolitan-libc: incredible project. Already knew of it, its awesome to offer a binary that runs in all S.Os at the same time.
Evaluate sizeof at compile time by causing duplicate case error: ha, nice trick for debugging the size of anything.
>Static array indices in function parameter declarations: awesome, a shame that C++ (and Tiny C) do not support it >/
The first array size is actually always decayed to a pointer, supporting it in a compiler without analysis passes like TCC is just a matter of skipping the "static" token and the size.
very interesting comment considering I'm literally fighting with stupid languages with this kind of permissive rules right now, which definitely just create more bugs (for instance silently dropped values because an upstream API changed, added an element at the end of the list, you updated but since you get no error you now have to go through all the calls to check them one-by-one)
Remember, in C you cannot use anything but a literal constant for the array size. My reference for how useful strict array-size matching can be under such circumstances is standard Pascal (as opposed to Modula-ish Pascals like Borland’s), and the answer there is that it more or less isn’t. Even in C, I’d expect at least some people would actually use things like int(*array)[5], given this syntax is valid even in C89, but in function signatures I’ve literally never encountered it.
If the size could a (type-level) variable, that would be a very different proposition. But variables lead to expressions, expressions lead to functions, functions lead to suffering^W becoming a full-fledged dependently typed programming language—if not an Agda or an Idris then at least an ATS[1]. I’d welcome that, but as far as I can see the ergonomics are very much not there yet for any kind of low-level programming.
> If the size could a (type-level) variable, that would be a very different proposition. But variables lead to expressions, expressions lead to functions, functions lead to suffering^W becoming a full-fledged dependently typed programming language
I mean, that's just plain old C++. You could have compile-time expressions operating on compile-time numbers since the 90s ; it's a widely-used feature.
It is, an array and a pointer are different types. There could be ways to convert it to a pointer, but it shouldn’t happen at so many places, implicitly.
Why is Golang awesome? Because in 2024 there is now a decent mock clock library for testing? I thought that one of go's selling points was the stdlib and tooling being great out of the box?
I think some devs are fed up of Complexity and excessive abstractions.
C has very few language features, it's a very simple language -- while
all modern languages have new (complex and taxing) features.
It's kinda like a Minimalist movement, can we do the same Modern Mumbo Jumbo but in a simple, minimalistic C way?
GC and RAII? Nah, just use Arenas/Pools. Or don't use heap at all.
C is an unsafe language but modern tools get better and better every year,
with GCC doing really cool static analysis and finding buffer overflows at compile time.
See e.g. wonderfully named "A Simple, Possibly Correct LR Parser for C11" [0], specifically the opening discussion of the ambiguities in the C grammar. Never mind the semantics, which are quite divorced from the underlying hardware! I mean, PDP-11 had a carry flag, a double-wide multiplication instruction, a combined divide-with-remainder instruction (just as x86 does) yet those are unexposed in C.
Yes, but maybe I want to shoot myself in the foot or am willing to take the risk, it's my computer and nobody can tell me what to do with it. That's why I like C. I've been writing Python for almost two decades now, all the new languages are trying even more to tell me how to do things or that I'm doing them wrong, screw that, I don't want to be coddled or patronised by the language I'm using, I'm writing C.
I think parent means simple as in the language has very few facilities and the ones it gives you are very barebones (hence simple). C is simple in the sense that a screwdriver is simple compared to a power tool.
It doesn't mean that it is simple to use correctly. An expert might be able to accurately judge the torque achieved by hand, but a beginner can easily under- or over-torque things. Etc...
Frankly this is why I have trouble taking all this C advocacy seriously. Do managers really want their engineers using C? I understand how it can be pleasurable in the way that listening to vinyl is pleasureable. But my undersanding is that it takes years and years of experience to not make catastrophic errors in C. And that's a lot of risk.
> Nah, just use Arenas/Pools. Or don't use heap at all.
This will solve more than 90% of problems. I'll also add that replace C pointers with a "Fat Pointer" or Go-like slice struct and avoid the C stdlib. Then you'll have fixed 99% of issues.
Custom allocators are pretty useful, yes. And slices, although it would be nice to have an easy syntax for using them.
Still have null pointers though. So an optional type would be useful too. Billion dollar mistake and all. Oh, and you have to check errno, might want a better way to handle errors so that you have to check them, or at least acknowledge that they exist. Probably need a linter to make those stick, you really do want violating a nullability constraint to fail at compile time.
And yeah, you have to avoid libc now, but it has so much useful stuff! So we'd want a library that offers all that useful stuff, but uses our fancy custom allocators, and optionals, and error checks, and slices.
While we're dreaming, wouldn't namespaces be nice? Like you have a function do_bar on a struct Foo type, so there's this foo_do_bar function (almost like a method), it'd be nice to be able to just say foo.do_bar, y'know?
Of course, at that point, you've almost got a whole different language! But it can compile and link with C, so yeah, best of both worlds.
Err.. Why does one need to even use a different language to implement Arenas and Slices? Yes having syntax level support for them would indeed be nice (something like new languages like Odin and Zig are trying) but you can start using them today and make programming in C an order of magnitude better.