The biggest problem with C is that doesn't even have enough features to help you build the features and abstractions you need and want.
For example with C++ the language offers enough functionality that you can create abstractions at any level, from low level bit manipulation to high level features such as automatic memory management, high level data objects etc.
With C you can never escape the low level details. Cursed to crawl.
Back in 1994/95, I wrote an API, in C, that was a communication interface. We had to use C, because it was the only language that had binary/link compatibility between compilers (the ones that we used).
We designed what I call "false object pattern." It used a C struct to simulate a dynamic object, complete with function pointers (that could be replaced, in implementation), and that gave us a "sorta/kinda" vtable.
Worked a charm. They were still using it, 25 years later.
That said, I don't really miss working at that level. I have been writing almost exclusively in Swift, since 2014.
> We designed what I call "false object pattern." It used a C struct to simulate a dynamic object, complete with function pointers (that could be replaced, in implementation), and that gave us a "sorta/kinda" vtable.
You were not alone in this. It is the basis of glib's GObject which is at the bottom of the stack for all of GTK and GNOME.
Sure, that's a pretty common pattern in use in C to this day. It's a useful pattern, but it's still all manual. Forget to fill in a function pointer in a struct? Crash. At least with C++ it will fail to compile if you don't implement a method that you have to implement.
You still need to think about error handling, and it's not standardized because everyone else will also have to think about it ad hoc.
You'll also still need to think about when to copy and move ownership, only without a type system to help you tell which is which, and good luck ensuring resources are disposed correctly (and only once) when you can't even represent scoped objects. `goto` is still the best way to deal with destructors, and it still takes a lot of boilerplate.
No, but that tends to gradually happen all by itself from my experience, even in solo projects. Because it feels stupid writing something yourself when you have access to stdlib. Also, I feel rolling your own stuff in C++ is a major pita in comparison to C, too many rules to memorize for me.
That pretty much is the definition of C. It was designed to be a system language, that was "one step beyond" (my own Madness reference) the assembler.
It's a dangerous tool, and should be wielded by experts. The same goes for all kinds of similar stuff.
And "experts" is kind of a darwinian thing. There's not really that many folks that can successfully wield it in large quantities (a certain cranky Finn comes to mind). There's a ton of folks that are surfing the Dunning-Kruger wave, that think they are up to it, but they tend to face-plant.
For myself, I learned to do it right, but it was difficult, stressful work, and I don't miss it. Working in Swift is a joy. I don't have to worry about things like deallocations, and whatnot.
I've never met an expert who writes c well all of the time, so I have been led to believe it's simply not possible. Folks with 20+ years of experience will still regularly write a use after free but which is not caught until it hits production. I'm not sure I've actually seen C code free of concurrency bugs. If it's possible it takes years of battle hardening with no feature work. Humans are prone to make mistakes and we need to use as many tools as possible to help us avoid making those mistakes. They are recently looking into introducing thread annotations for locks in the Linux kernel and I expect them to find lots of real bugs lurking in otherwise well maintained subsystems. I've rewritten swaths of code into rust and always seen it find a bug in the original code's design that we were lucky to never run into.
haha every segfault puts me back down into the valley of despair xD. thats totally ok!
higher level languages often make it so you can focus on the language mostly. C and assembly, i think, you need a lot of context. what is running the code, and how.
Of course you can. It's quite the opposite actually. The downside is that in C you have to code a bunch of abstractions _yourself_. See how large projects like the Linux kernel make extensive use of macros to implement an object system.
so confused by this one. C is the most free to build systems and abstractions. it doesnt lock you down into any paradigm so you can build your own....
you can abstract away perfectly fine low level details and use your own high level constructs to build what you want in a memory safe way...
high level daya objects?
sure it doesnt have garbage collection, so it motivates you not to leave garbage laying around for some slow background process to collect.
actually you can build this in C you just do not want to...
you can give all your objects reference counters and build abstractions to use that, you can implement 'smart pointers' if u want, and have threads do garbage collection if u want... why not? what exactly is stopping you?
maybe its less convenient to go that route. but impossible? nope.
connect GDB to your c++ program and see how it works... its not like c++ suddenly doesnt become machine code. and c translates perfectly fine to machine code...
Sure you can do this, but the point is that it's all manual and always will be because you get no help from the compiler.
Compare in C++ where you can have higher level types and compiler helpfully provides features that let the programmer do stuff such as RAII or unique_ptr.
For example with C++ the language offers enough functionality that you can create abstractions at any level, from low level bit manipulation to high level features such as automatic memory management, high level data objects etc.
With C you can never escape the low level details. Cursed to crawl.