The Unreasonable Effectiveness of C (2013)(damienkatz.net)
damienkatz.net
The Unreasonable Effectiveness of C (2013)
http://damienkatz.net/2013/01/the_unreasonable_effectiveness_of_c.html?
312 comments
Honestly, sometimes I think that the best solution for fixing the security quagmire we're in would have been not by creating a new system programming language like Go, Nim, Rust, D or even reviving one of the Wirthian languages (though having them on the table is very much a good thing), but by creating a memory safe dialect of ANSI C, and perhaps standardize on a better string handling library.
In fact, such an effort was started at AT&T back in 2001. The language was named Cyclone [1]. It met its 1.0 release in 2006. It has since been abandoned. Which is a damn shame, because it looked like it had real potential.
Is C a primitive language? Yes. In fact, it's almost stupidly simple compared to most modern languages. But its greatest weakness also proves to be its greatest strength. The main cognitive overhead from using C is in its manual memory management model and certain gotchas in standard libraries. It being bare bones as it is (but not too much) also makes it surprisingly pleasant at times. I get the feeling that plenty of modern languages try too hard to be novel and, for all the freedom you get from not having to worry about anything below program logic itself, you still end up expending a lot of effort into complicated high-level patterns that are frequently interspersed with arcane PL theory.
But that's not even the main reason why something like Cyclone is much needed. No. It's that we've pretty much perfected a ton of our APIs, like POSIX, to work with C. It simply feels most natural to work with POSIX in plain C. FFIs and bindings vary, some of them like Nim's actually feel quite clumsy (otherwise Nim makes it absolutely trivial to create FFIs, which is great).
Finally, refactoring 30 years worth of code into Cyclone sounds far more realistic than rewriting the world in Rust.
[1] https://en.wikipedia.org/wiki/Cyclone_%28programming_languag...
In fact, such an effort was started at AT&T back in 2001. The language was named Cyclone [1]. It met its 1.0 release in 2006. It has since been abandoned. Which is a damn shame, because it looked like it had real potential.
Is C a primitive language? Yes. In fact, it's almost stupidly simple compared to most modern languages. But its greatest weakness also proves to be its greatest strength. The main cognitive overhead from using C is in its manual memory management model and certain gotchas in standard libraries. It being bare bones as it is (but not too much) also makes it surprisingly pleasant at times. I get the feeling that plenty of modern languages try too hard to be novel and, for all the freedom you get from not having to worry about anything below program logic itself, you still end up expending a lot of effort into complicated high-level patterns that are frequently interspersed with arcane PL theory.
But that's not even the main reason why something like Cyclone is much needed. No. It's that we've pretty much perfected a ton of our APIs, like POSIX, to work with C. It simply feels most natural to work with POSIX in plain C. FFIs and bindings vary, some of them like Nim's actually feel quite clumsy (otherwise Nim makes it absolutely trivial to create FFIs, which is great).
Finally, refactoring 30 years worth of code into Cyclone sounds far more realistic than rewriting the world in Rust.
[1] https://en.wikipedia.org/wiki/Cyclone_%28programming_languag...
C is a great language, for fun I'm slowly working on my own highly C like langage - https://github.com/g-programming/glang.
Its basically C with Go like syntax, and a package system instead of headers. I aim to fully interop with existing C code and have no runtime requirements beyond what C has.
I'm not trying to go crazy with extra features over what C has though. An important part of what will make it work, is perfect interop with existing C code.
Yes I understand memory safety issues, but there is always a need for machine independent assembly languages. which C essentially is.
Its basically C with Go like syntax, and a package system instead of headers. I aim to fully interop with existing C code and have no runtime requirements beyond what C has.
I'm not trying to go crazy with extra features over what C has though. An important part of what will make it work, is perfect interop with existing C code.
Yes I understand memory safety issues, but there is always a need for machine independent assembly languages. which C essentially is.
This last bit is so true:
> I always have it in the back of my head that I want to make a slightly better C. Just to clean up some of the rough edges and fix some of the more egregious problems.
I'm always mulling over that. "Maybe just namespaces, better macros, function overloading and member functions (eg this->) would work." Yet I'm sure Bjarne Stroustrup was thinking the same thing when he started on "C with Classes." The power to add features must be so overwhelmingly corruptive. So justifiable in each and every case, in the moment. And then you just can't stop. "Closures would be helpful. I'd love to write generic containers. Inheritance would save so much typing.", and the next thing you know, you're working on virtual inheritance, concepts and user-defined literals. :/
In my opinion, C really does need just a bit more to be great, but it would need someone with a will of steel to stop and leave things be after that.
> I always have it in the back of my head that I want to make a slightly better C. Just to clean up some of the rough edges and fix some of the more egregious problems.
I'm always mulling over that. "Maybe just namespaces, better macros, function overloading and member functions (eg this->) would work." Yet I'm sure Bjarne Stroustrup was thinking the same thing when he started on "C with Classes." The power to add features must be so overwhelmingly corruptive. So justifiable in each and every case, in the moment. And then you just can't stop. "Closures would be helpful. I'd love to write generic containers. Inheritance would save so much typing.", and the next thing you know, you're working on virtual inheritance, concepts and user-defined literals. :/
In my opinion, C really does need just a bit more to be great, but it would need someone with a will of steel to stop and leave things be after that.
"C has the fastest development interactivity of any mainstream statically typed language."
This is not true. Using debuggers and crash dumps to reason about your running program is not fast or interactive at all. It doesn't matter how good the tools are. They interrupt the workflow.
Working in a REPL on the other hand, is real interaction with your program. You speak the language to the program and the program responds in the same language. This is fast.
This is not true. Using debuggers and crash dumps to reason about your running program is not fast or interactive at all. It doesn't matter how good the tools are. They interrupt the workflow.
Working in a REPL on the other hand, is real interaction with your program. You speak the language to the program and the program responds in the same language. This is fast.
"You wanted a banana but what you got was a gorilla holding the banana and the entire jungle."
- Joe Armstrong
One could argue, easily, this is true for many of the popular garbage collected languages. It is certainly why I do not care for them. It is also why I do not care for some of the popular OS's, or most of the popular "software solutions". It is the unwanted delivery of the kitchen sink when all I want is a glass of water.
The OP says he's frustrated with C "because it seems to be trying (with limited success) to move to being a higher level language. There are times when I really do want a "thin layer over assembly", and C seems to have evolved to a place where that is no longer possible."
"seems to have evolved"
Some of my favorite programs that continue to work reliably, year after year, are written in what I guess some would call "primitive," K&R-like C and with little need for "the" standard libraries. I am not an expert C programmer, but I have no complaints when reading and editing this "primitive" C. I have seen others complain about it in forums.
One of my favorite source code comments is in the netcat source where the author says he's going to write the program as if he's writing in assembly. In my mind C should be just a thin layer over assembly. Essentially, to me, it should be a kind of "shorthand" for writing assembly. (That implies the author should think as if he's writing assembly. And C just takes away the drudgery of actually typing out all those lines of assembly instructions.) Now, that is just my opinion. I am not an expert C programmer. I know nothing.
- Joe Armstrong
One could argue, easily, this is true for many of the popular garbage collected languages. It is certainly why I do not care for them. It is also why I do not care for some of the popular OS's, or most of the popular "software solutions". It is the unwanted delivery of the kitchen sink when all I want is a glass of water.
The OP says he's frustrated with C "because it seems to be trying (with limited success) to move to being a higher level language. There are times when I really do want a "thin layer over assembly", and C seems to have evolved to a place where that is no longer possible."
"seems to have evolved"
Some of my favorite programs that continue to work reliably, year after year, are written in what I guess some would call "primitive," K&R-like C and with little need for "the" standard libraries. I am not an expert C programmer, but I have no complaints when reading and editing this "primitive" C. I have seen others complain about it in forums.
One of my favorite source code comments is in the netcat source where the author says he's going to write the program as if he's writing in assembly. In my mind C should be just a thin layer over assembly. Essentially, to me, it should be a kind of "shorthand" for writing assembly. (That implies the author should think as if he's writing assembly. And C just takes away the drudgery of actually typing out all those lines of assembly instructions.) Now, that is just my opinion. I am not an expert C programmer. I know nothing.
So, is Rust a better C? I haven't dug into it too much but that's been my general impression of how it's been presented.
"C has the fastest development interactivity of any mainstream statically typed language."
I wonder if this statement is true since the invention of Go. A very fast compile was one of the design goals of Go, and its authors could afford luxuries (esp. In terms of memory use and object file size) that were unthinkable in the days of C. For example, IIRC object files contain the compiled code of their source plus the top hierarchy of modules called.
People have mentioned that changing a .h file in a large C project could trigger a lengthy recompile. AFAIK, Go minimizes this problem, so if the hype is correct there should be lots of projects where similar situations will be handled faster in Go.
My own empirical experience is very limited. I can confirm that Go compiles much faster than Java for my code, but that will surprise no one and it doesn't help us compare with C.
Does anybody have more tangible data regarding Go's alleged speed demon nature esp. as compared to C when compiled with e.g. VS or gcc?
I wonder if this statement is true since the invention of Go. A very fast compile was one of the design goals of Go, and its authors could afford luxuries (esp. In terms of memory use and object file size) that were unthinkable in the days of C. For example, IIRC object files contain the compiled code of their source plus the top hierarchy of modules called.
People have mentioned that changing a .h file in a large C project could trigger a lengthy recompile. AFAIK, Go minimizes this problem, so if the hype is correct there should be lots of projects where similar situations will be handled faster in Go.
My own empirical experience is very limited. I can confirm that Go compiles much faster than Java for my code, but that will surprise no one and it doesn't help us compare with C.
Does anybody have more tangible data regarding Go's alleged speed demon nature esp. as compared to C when compiled with e.g. VS or gcc?
(2013)
First, I think the speed of C relies heavily on the compiler. C is not fast, not if I wrote the compiler. But GCC is very fast, and ICC at least as much.
I was a little confused by the Erlang wariness, but the reasoning seemed sound
> At Couchbase we recently spent easily 2+ man/months dealing with a crash in the Erlang VM. ... ... it was a race condition bug in core Erlang. We only found the problem via code inspection of Erlang.
That does not sound like a fun time, but once the bug is fixed, what's the big deal? Is there good reason to believe more bugs will come? It wasn't my ass that got bitten but I'd forgive Linux even if it had a race condition bug. (or if it crashes touching 1000 files in /etc)
First, I think the speed of C relies heavily on the compiler. C is not fast, not if I wrote the compiler. But GCC is very fast, and ICC at least as much.
I was a little confused by the Erlang wariness, but the reasoning seemed sound
> At Couchbase we recently spent easily 2+ man/months dealing with a crash in the Erlang VM. ... ... it was a race condition bug in core Erlang. We only found the problem via code inspection of Erlang.
That does not sound like a fun time, but once the bug is fixed, what's the big deal? Is there good reason to believe more bugs will come? It wasn't my ass that got bitten but I'd forgive Linux even if it had a race condition bug. (or if it crashes touching 1000 files in /etc)
C was built as a replacement for assembly programming. It seems low-level today but it does a huge amount of work for you. Call stack manipulation, computing offsets for nested struct members, converting arbitrarily complex conditionals into the right branching instructions, coalescing a sequence of expressively named intermediate values into the same machine register... these are the things C does for you. I've never written a serious assembly program but I can imagine how annoying it is to do all that stuff manually. It would be easy to screw up.
I think C has been so successful because it made really good decisions about what to do for you. It eliminated a lot of those busy-work programming tasks without taking away your fundamental control of the machine. Automatic array bounds checking, garbage collection, ... they would have been unthinkable for the original goals of C. The stupid warts are annoying but I think C made fundamentally sound choices of what to do automatically and what to leave up to the programmer.
We all talk about modular, abstract code, but back in the day "modular" meant "use the same calling convention for every int(int, int) function", not "dependency injection". From that perspective, C must have felt like a huge leap in modularity and abstraction from assembler.
I think C has been so successful because it made really good decisions about what to do for you. It eliminated a lot of those busy-work programming tasks without taking away your fundamental control of the machine. Automatic array bounds checking, garbage collection, ... they would have been unthinkable for the original goals of C. The stupid warts are annoying but I think C made fundamentally sound choices of what to do automatically and what to leave up to the programmer.
We all talk about modular, abstract code, but back in the day "modular" meant "use the same calling convention for every int(int, int) function", not "dependency injection". From that perspective, C must have felt like a huge leap in modularity and abstraction from assembler.
One nitpick:
> That some of the most heavily used and reliable software in the world is built on C is proof that the flaws are overblown, and easy to detect and fix.
Easy to detect? Multiple times per year we find out about a security bug in Linux or Windows that has existed since the 90s.
> That some of the most heavily used and reliable software in the world is built on C is proof that the flaws are overblown, and easy to detect and fix.
Easy to detect? Multiple times per year we find out about a security bug in Linux or Windows that has existed since the 90s.
Some of the unhappiness stems from C++, which was supposed to "fix C". Instead, it created a big mess. C++ is the only major language to support hiding ("abstraction") without memory safety. This turned out to be a poor feature combination, never again repeated. C has neither hiding nor memory safety, so if it's broken, at least it's visible at the user program level. Java/Pascal/Modula/Ada/Common LISP/Go/Rust, and the interpretive "scripting languages", all have both hiding and memory safety. We've at least come to an agreement on that feature set.
Because of the existence of C++, mainstream C language development more or less stopped. ANSI C, ISO C, C99, and C11 don't differ by much.
(At one time, I was promoting an approach to make C memory-safe in a way that allowed mixing safe and unsafe modules to allow a gradual transition and rewriting of old code. See (http://animats.com/papers/languages/safearraysforc43.pdf). It's technically feasible but politically hopeless. I'm now pinning my hopes on Rust and hoping they don't screw up.)
Because of the existence of C++, mainstream C language development more or less stopped. ANSI C, ISO C, C99, and C11 don't differ by much.
(At one time, I was promoting an approach to make C memory-safe in a way that allowed mixing safe and unsafe modules to allow a gradual transition and rewriting of old code. See (http://animats.com/papers/languages/safearraysforc43.pdf). It's technically feasible but politically hopeless. I'm now pinning my hopes on Rust and hoping they don't screw up.)
The reason I love C——I wouldn't like to love it but I am dragged down back to C each time so I think I've just given up——is that it costs a bit more to write.
I used to prototype things with Python by the method of sheer experimentation. When I found a suitable design, I'd rewrite it "properly" in Python and if that wasn't fast enough I'd think a bit and rewrite it in C. But that's happening less and less often these days.
Because C costs a bit more to write it makes me think ahead. And I've (once again) found that while prototyping in Python is fast it's even faster to think and then just write C. I write some boilerplate C and start adding the data structures... this could take a "long" time but once I've figured it out, writing the actual C code is a breeze. All the mental work has already been done and once the data structures are right, the implementation just follows.
Curiously, this doesn't exactly happen with Python. Because you can make a Python program run almost immediately, there's nothing forcing you to the productive idleness. In C, you'll have to write a bit of code first until you can get something meaningful up and running: this is the hatching time for what you really want to write.
I'm not sure why I haven't observed this with other languages. I suppose I would need to write hefty amounts of Java until I could get a program running somewhat, but this process of forced productive idleness mostly happens in C. Maybe it's because there are no layer beneath C and assembly and you intuitively know the tradeoffs and only a negligible delta of what you write and what the machine will actually do. Maybe it's because C feels so physical. I don't know. But so far C is the only language that gives me that process in a native, intuitive way, constantly telling me that "you must not rush, because you can't rush anyway".
I used to prototype things with Python by the method of sheer experimentation. When I found a suitable design, I'd rewrite it "properly" in Python and if that wasn't fast enough I'd think a bit and rewrite it in C. But that's happening less and less often these days.
Because C costs a bit more to write it makes me think ahead. And I've (once again) found that while prototyping in Python is fast it's even faster to think and then just write C. I write some boilerplate C and start adding the data structures... this could take a "long" time but once I've figured it out, writing the actual C code is a breeze. All the mental work has already been done and once the data structures are right, the implementation just follows.
Curiously, this doesn't exactly happen with Python. Because you can make a Python program run almost immediately, there's nothing forcing you to the productive idleness. In C, you'll have to write a bit of code first until you can get something meaningful up and running: this is the hatching time for what you really want to write.
I'm not sure why I haven't observed this with other languages. I suppose I would need to write hefty amounts of Java until I could get a program running somewhat, but this process of forced productive idleness mostly happens in C. Maybe it's because there are no layer beneath C and assembly and you intuitively know the tradeoffs and only a negligible delta of what you write and what the machine will actually do. Maybe it's because C feels so physical. I don't know. But so far C is the only language that gives me that process in a native, intuitive way, constantly telling me that "you must not rush, because you can't rush anyway".
C is a great language but have you tried go? Feels very c like with a lot of very nice conveniences.
What's a good practical way to get my feet wet with C? I want to learn it, but I'm the sort of person that can only learn a language by actually trying to build something useful in it. Sometimes it seems like a language that everyone thinks is important to know, but that no one thinks should actually be used in a real world project unless you're working on embedded systems or building an OS. What are some ideal (and beginner-friendly) use cases?
It claims C has the fastest build/run cycle. Is this true? Many non-compiled languages I can redefine my functions at runtime, and with Java I can hotswap.
There is currently a really cool project I suggest (if you're interested in learning / seeing C in action) called Handmade Hero[1].
He[2] is making a game, from scratch, in (mostly) C (but with a little bit of C++ niceties thrown in), and streaming[3] the entire process for an hourish, Monday -> Friday. It's intended to be a tutorial in both learning C and learning from-scratch game development (where from scratch means using GDI and like, parsing your own wav files).
It's really cool to watch, he's an entertaining guy, afaict is a pretty decent teacher (I know zero C, but C-style languages, and I _think_ I'm keeping up), so I recommend checking it out.
[1] http://handmadehero.org/ [2] https://twitter.com/cmuratori [3] http://www.twitch.tv/handmade_hero
He[2] is making a game, from scratch, in (mostly) C (but with a little bit of C++ niceties thrown in), and streaming[3] the entire process for an hourish, Monday -> Friday. It's intended to be a tutorial in both learning C and learning from-scratch game development (where from scratch means using GDI and like, parsing your own wav files).
It's really cool to watch, he's an entertaining guy, afaict is a pretty decent teacher (I know zero C, but C-style languages, and I _think_ I'm keeping up), so I recommend checking it out.
[1] http://handmadehero.org/ [2] https://twitter.com/cmuratori [3] http://www.twitch.tv/handmade_hero
Every time I see someone praising C I curse Microsoft for not having proper support for C99 in Visual Studio.
The boring response would be that different languages have different strengths. C is indeed capable of being very fast, mainly by virtue of letting the dev have fine control over memory. But execution speed isn't the only metric. Sometimes you just need something to be done quickly and you know you aren't going to hit the constraints (time, space) of the system, so you might as well go with something like .NET to reduce your development time.
The point about the tooling I'm not so sure about. There's a lot of languages out there with well developed tools. And the tools tend to address the pain points: you have GC profiling in .NET, and you have Valgrind-type tools in c++.
The point about the tooling I'm not so sure about. There's a lot of languages out there with well developed tools. And the tools tend to address the pain points: you have GC profiling in .NET, and you have Valgrind-type tools in c++.
"I always have it in the back of my head that I want to make a slightly better C. Just to clean up some of the rough edges and fix some of the more egregious problems."
This is called Go. Brought to you by the same people who gave you C.
This is called Go. Brought to you by the same people who gave you C.
C is not the fastest language. C++ is faster than C. For example, the only way you can hope to match C++ inline template algorithms in C is with a horrific macro scheme.
It’s as if people make asymptotic progress towards the goal of the ‘ultimate programming language’, the newer innovations which may well have improvements are too marginal to be beneficial, there is too much existing inertia, that is why the more prominent modern ones incorporate interoperability with C often. It would seem more sensible to say as the author of this post seems to propose, that C is the de facto ultimate.
> C is the fastest language out there.
I thought it was Fortran.
I thought it was Fortran.
In this thread I saw no mention of Freebasic. It has some good features and is comparable to C. TerraLang is another interesting approach (for system programming languages).
But the fundamental flaw is that in many cases a DSl that compiles to something lower level is the way to Go.
But the fundamental flaw is that in many cases a DSl that compiles to something lower level is the way to Go.
one of the best things C has to offer was omitted herein - the preprocessor ;-)
Hmmm. I work on a pretty large scientific computing project written in C++. Complex numbers in C++ are much, much better than they are in C. In general, I'm very happy it isn't in C.
Remember that where there's C, there can be Lua.
Just saying.
Just saying.
call us when the honeymoon is over.
It's basically impossible to write safe code in C. Surely that makes it pretty damn poor at what it does?
Especially since system code is the place where safety matters most.
Especially since system code is the place where safety matters most.
>Every time there is a claim of "near C" performance from a higher level language like Java or Haskell, it becomes a sick joke when you see the details. They have to do awkward backflips of syntax, use special knowledge of "smart" compilers and VM internals to get that performance, to the point that the simple expressive nature of the language is lost to strange optimizations that are version specific, and usually only stand up in micro-benchmarks.
I see this kind of statement a lot, and it simply does not hold up to even a cursory look. Where are the awkward backflips of syntax, special compiler or VM knowledge, or version specific optimizations here?
http://benchmarksgame.alioth.debian.org/u32/benchmark.php?te...
http://benchmarksgame.alioth.debian.org/u32/benchmark.php?te...
Why are there so many java web frameworks/libraries/servers that offer performance close to C?
http://www.techempower.com/benchmarks/#section=data-r9
>Critically important to developer efficiency and productivity is the "build, run, debug" cycle.
This is a pretty bizarre claim. Sure, having an equally archaic and primitive process but 10 times slower is obviously worse (C++), but that doesn't mean having an archaic and primitive process is good if it is fast. I rarely ever run my code, much less debug it. Freeing myself from that horrible way of working was one of the greatest things about working in a language with a useful type system.
I see this kind of statement a lot, and it simply does not hold up to even a cursory look. Where are the awkward backflips of syntax, special compiler or VM knowledge, or version specific optimizations here?
http://benchmarksgame.alioth.debian.org/u32/benchmark.php?te...
http://benchmarksgame.alioth.debian.org/u32/benchmark.php?te...
Why are there so many java web frameworks/libraries/servers that offer performance close to C?
http://www.techempower.com/benchmarks/#section=data-r9
>Critically important to developer efficiency and productivity is the "build, run, debug" cycle.
This is a pretty bizarre claim. Sure, having an equally archaic and primitive process but 10 times slower is obviously worse (C++), but that doesn't mean having an archaic and primitive process is good if it is fast. I rarely ever run my code, much less debug it. Freeing myself from that horrible way of working was one of the greatest things about working in a language with a useful type system.
And the followup discussed here: https://news.ycombinator.com/item?id=5075370
I repost because I'm becoming more and more frustrated with C, and wondered if others feel the same. Not because it's too low-level, rather because it seems to be trying (with limited success) to move to being a higher level language. There are times when I really do want a "thin layer over assembly", and C seems to have evolved to a place where that is no longer possible. I explain my distress more completely in the comments of Yann's article here: http://fastcompression.blogspot.com/2014/11/portability-woes...