Flux RSS

Why C is a good language - or why you're wrong thinking C++ is better

I've recently had a discussion with some friends over at Google+ which finally made me understand why there's so many people out there who don't understand why C is superior to C++, even though there's so many people who say so. So let me share this epiphany with you.

The reason why some programmers think C++ is superior to C is because they're bad programmers. Alright, let me rephrase this so you don't immediately start flaming me. The reason why some programmers think C++ is superior to C is because they (most of the time) don't have the same mindset. And if they do have the same mindset as pro-C people, then they are not aware that there's people out there who don't. But allow me to develop.

Basically, I came to realize that a lot of programmers out there do not get the concept of "pay only for what you use". Which, inherently, make them "bad" programmers. I say "bad" as in "they can't write code that is efficient". These programmers might come up with very nice (as in, pretty) pieces of software. They also might come up with very stable piece of software. But they can't come up with code that is at least as efficient than code written by the programmers who know exactly the advantages of C over C++.

The main issue at hand is "quality of life". I now understand that there is a lot of programmers out there who are only "consumers". They consume compilers and languages (and, as an extend, processors) as they see fit for their convenience and not as they see fit for the efficiency of the final software. They only want to "get the job done", and do it in the most efficient manner for the number of lines they have to write, or the easiness of the code they write.

But a "real" programmer will actually be aware of the generated code. They do not care about their quality of life, as long as the generated software is slim and efficient. These programmers will know exactly what kind of assembly code is going to be generated as they write the source code. They are perfectly aware of each and individual cost of their line of code in terms of generated code size and speed. And C is the only language that fully provides this ability. Linus original argument can be translated into "if you are a C++ advocate for the sole reason it's easier to use for some programmers, then I don't want you to contribute to git, because you're most likely to be not aware of code efficiency, and thus, you're most likely to contribute non-efficient code into a project where efficiency is the main selling point."

Now, in an environment where the code is fully controlled, and where code contributors are fully respected and trusted, then C++ might be acceptable, because then one can trust them to do the right choices, and dodge the various pitfalls that C++ will present them. But in the case of open-source softwares, such as the Linux kernel, or git, where you can't possibly fully review every and each contribution being made, because there's so many, then C presents the advantage of fencing yourself against common bad programming practices that Object-Oriented programmers tend to be plagued with.

If you're a "good" programmer (as in, you know exactly what is the efficiency cost you're paying with the code you're currently writing) who still is a C++-advocate, then you're just ignoring the fact that there is bad programmers out there (as in, programmers who don't know or care about the efficiency of their code). Whilst I'm not saying it's absolutely impossible to do code in C++ that is as efficient as C code, I'm saying it's way easier to do insanely horribly non-efficient code in C++ than in C, because of the various pitfalls it presents to programmers. So if you're a "good" C programmer, you're probably a "good" C++ programmer too (as in the code you'll write in C++ will be as efficient as the code you'll write in C) . But if you're a "good" C++ programmer to start with (as in, you know how to do pretty things in C++, and write some insanely convoluted Object-Oriented C++ code), then it's not guaranteed you'll be able to write efficient code.

Of course, the notion of "good" and "bad" programmer diverges depending on the actual goal that's being desired for the job. But then C++ is still a horrible language on a lot of fronts: it's a bad Object-Oriented language - there's plenty of other languages out there who are doing a much, much better job than C++ to create Object-Oriented idioms - and it's a language that introduces so many pitfalls on top of C that the average programmer will write C++ code that is much, much less efficient.

In conclusion, if you don't like C, and prefer more modern languages, then I'm not saying you're a "bad" programmer as it's very probably you'll write code that is pretty, and runs nifty GUIs and even may be useful. However, I'm saying that if you don't see the advantages of C over other languages, then it means you don't clearly understand the philosophy behind C, and what it actually means to write code that's going to be as efficient as possible. Start learning about compilers. Learn about assembly. Learn about processors, cache lines, and other branch-predictions thingies. Then you'll understand why C is superior.

Comments

1. On Friday, March 2 2012, 18:24 by Dave Cahill

"Start learning about compilers. Learn about assembly. Learn about processors, cache lines, and other branch-predictions thingies."

Book / article recommendations for these topics might be a good follow up post?

2. On Friday, March 2 2012, 19:19 by Pixel

That is a very good point. I remember learning programming and "things about computers" using a book called "The PC Bible", which taught me an insane lot about assembly, disassembly, and how the computer operates.

Unfortunately, this is way more complicated today, because computers are now way more complex than 10 or 15 years ago. I guess a lot of people evolved from 8086 up to today's architecture. If you'd want to start today, the learning curve might be very steep.

I'd say a good starting point would be to work with old, or maybe easier architectures. Something like the MIPS that can be found in the PlayStation2 is an excellent starting point. Microcontrollers might also bring a very good learning platform for understanding what the compiler does, and the impact of your code on the chip. Learning ARM microcontrollers, such as the mBed platform, could be a nice entry point too. A good counter-example is the Arduino platform, where all the code is written in C++, without any consideration for the fact the chip the code is supposed to run onto is very limited in space and speed.

3. On Saturday, March 3 2012, 13:23 by biou

Your argument reminds me of the Joel Spolsky's law of leaky abstractions. http://www.joelonsoftware.com/artic...
C++ abstracts you some part of the underlying reality and as such it is more difficult to write efficient code.
Last time we met, we talked about objective-c and why it is an extremely bad language (read inefficient). I tried and found the language funny, but I am not a good programmer. It is some kind of dynamic language and it has a cost, the cost of efficiency.

4. On Sunday, March 4 2012, 00:55 by Cerales

I think it's pretty obnoxious to suggest that people who are writing software which doesn't share the requirements of software that has to be written in C are "bad programmers". Computers are fast and it's not a zero-sum game. Of course our kernels and our low-level networking and graphics layers should be in C. Why should some web server I'm writing be in C? Or an automation system I'm writing for my work? Come on, tell me why I am possibly a "bad programmer" for understanding the different characteristics of, say, Python and C, and choosing the one that's right for the job (in that people have limited time and must balance priorities).

If I came into work tomorrow and my boss asked whether I could have our system updated by lunch, and I told him no it'll take until Wednesday because I'm going to do it "correctly" in C (even though it runs on a dedicated machine that is never at load), I'd correctly be lampooned as an awful neckbearded powersperge.

5. On Sunday, March 4 2012, 02:13 by Steve

Using C++ does not make a person ignorant of performance or unaware of the resulting generated code. Of course there are programmers out there like that - there are bad programmers for any language you care to name. Given such things as the Obfuscated C Contest and the infamous history of buffer overruns and pointer-related errors leading to crashes, exploits etc, C programmers should be careful about claiming that the existence or even prevelance of bad programmers proves a language bad.

One of the things Stroustrup emphasises is that by using template functions for algorithms, you get better performing, lighter weight code than you do with e.g. the qsort function in C. In the C version, you pay for a run-time polymorphism mechanism - calling through a function pointer for every comparison - and you pay even more for the fact that this prevents inlining and various optimisations.

Of course templates come with a risk of bloat. But being aware of that risk means you can manage it, choosing to add run-time polymorphism mechanisms as and when you need them. There's not much of that in the standard library, but Stroustrup has reasons for considering that run-time polymorphism is overused.

Stroustrup implies that in C, qsort with its run-time polymorphism overhead is the only option other than perhaps cut-and-paste coding. Of course he's ignoring macros, which are not unrelated to templates. For example, overuse of macros can cause bloat.

AFAIK, though, the C standard library doesn't include a sorting macro.

In his rant a few years ago, Torvalds makes a big deal about the hiding of data structures. Well, personally, a common pattern in my code is a "tool class". That's a class which provides a collection of functions for, e.g., working on a data structure, but which doesn't contain, own or hide that data structure. A tool class may be polymorphic in the simple template sense, in the virtual functions sense, or in the template with policy parameter sense (e.g. allowing the same code to be switched from compile-time to run-time polymorphism by changing a single template parameters). I may have several tools for working on the same data structure, each re-usable for different cases - one caring about ordered keys, one caring about some other summary (number of items in the subtree, perhaps, for efficient subscripting etc), one caring about nothing but the links perhaps. And sure, you need to set up several tools for one job - just as you'd need to set up several parameter blocks to do the same kind of thing in C.

Using a thin container-class wrapper makes a lot of sense for hiding this hassle, but that's all it hides. You're not blocked from re-using anything useful - you can still re-use the tool classes. You get the best of both worlds - the container abstraction that doesn't expose its implementation *and* the internals that can be re-used for other related but non-identical data structures.

The term "tool class" is AFAIK mine, but it's really just a generalisation of other common patterns. A factory class, for example, is an allocation and initialisation tool. An iterator (the OOP pattern - not really STL iterators) is a traversal tool. The object doesn't allocate itself or traverse itself any more than the shape draws itself. These ideas have been out there in well-known and widely used patterns for quite some time.

Of course Torvalds can still point to the standard library. We all know that std::map is most likely a red-black tree, but we can't re-use those internals to create augmented red-black tree data structures. But since the standard library doesn't even guarantee that these are red-black trees, that makes sense. These containers aren't meant to be all things to all people, any more than qsort is meant to be the only sort you'll ever need. If your business depends on your data structures, of course you will write your own data structures. And nothing about C++ forces you to hide your data structure implementation code inaccessibly within container classes. Though it's sadly true that doing that isn't widely recognised as the anti-pattern that I think it is.

6. On Sunday, March 4 2012, 06:55 by Ephrahim

C++ is a better language. This is because it was developed after C and consequently fixes the problems discovered in the C language.

C++ is much faster and is able to manage dynamic programming. C is static language with static types and cannot be used for online programming software.

7. On Sunday, March 4 2012, 08:03 by Cerales

Ephrahim, that's going too far in the other direction.

Can't we just accept that all these languages have characteristics which are (somethings qualitatively) different from each other? I mean I'm not trying to be a massive hippy about this but there is clearly an unambiguously a large use case for both C and C++, and for higher-level languages.

8. On Sunday, March 4 2012, 10:25 by Pixel

@Ephrahim: Windows ME was developed after Windows 98, therefore it must be a better Operating System, right ? Oh, wait...

@Cerales: I'm not saying you're a bad programmer if you're able to chose the right tool for the right job. In fact, if you're looking at my various repositories, there is a mix of C, C++, Lua, etc... I am saying that there are people out there who assess that everything should be pure C++ and that C is obsolete and anyone who still do C code are just old geezers. These people are probably just not good coders enough to see the advantages of C over C++ - which pretty much sums up as "not being able to chose the right tool to do the right job".

Also, I didn't develop much into this, but you should read the various posts I linked at the top. Mike Acton is definitely somebody you want to follow. You'll discover that C++ actually alienated a lot of good programers with very bad programming habits. One thing that C++ doesn't help you with for example is coding for the case of n=1, and then expand at will. Turns out, this ruins performances. Understanding your data, having a grasp of what goes where in memory, being able to organize your data by grouping it so to make sure you're now blowing up your cache at every access you make, all of what C++ is definitely NOT helping you with. Sure, you may not need efficiency in what you're actually doing right now, but it's sad to lose focus about doing code that doesn't burn our planet's energy for nothing.

C++ programmers tend to forget (or never learned ?) how processors work, how memory caches work, and how devices work. If you're very highly performances-bound, and still using C++, you're going to have to be extremely careful to what exact features of C++ you're using. And trust me: performances are important at every level of your applications. Otherwise you might end up with things like a video game that performs only at 20fps on a full-blown XBox360, or even a web server that serves only 100 queries per second.

Again: I'm not saying it's fully impossible to write efficient code in C++. I'm saying it's way, way easier to get lazy and lost in C++'s warm and comfortable arms, and lose focus of some of the most basic aspects of "what makes a piece of code efficient" than in C. And I'm not saying either that it's impossible to do very stupid things in C too. The best counter-example being Apache's httpd. But the mindset of C programmers is usually more tuned towards the little things that make the difference in CPU usage.

Case in point actually: I challenge anyone to show me any high-performance-oriented project that is written in full-blown C++. It occurs to me that it isn't much of a coincidence that varnish, git or the linux kernel are written in plain-C. And again: it's not because it's impossible. But because it's more difficult to lose focus about performances if you do plain-C than if you step into the land of C++.

9. On Sunday, March 4 2012, 13:11 by Steve

@Pixel - there are OTT advocates who insist any particular language is the one true language, C included. Those extremist C advocates are just as guilty of not choosing the right tool for the job.

Consider the following - http://www.perl.com/pub/2000/12/adv...

In my earlier comment, I mentioned qsort and it's run-time polymorphism overhead. I have libraries written in C++ right now that suffer from a lot of this. That's because they wrap heavily C-style code under a thin typesafe wrapper. I'm not talking about refusing to use classes - I'm talking about using offsetof, pointer arithmetic, function pointers etc - low-level techniques to achieve the goal of run-time polymorphism, keeping code small, but with a performance penalty to pay for that.

Interesting fact - in C++, using that low-performance run-time polymorphism is relatively difficult. No more difficult that in C, but with high-performance compile-time polymorphism being so easy, arguably C++ is the performance language.

You talk about knowing how the machine works. I've encountered plenty of C programmers who still believe that every access to memory takes the same amount of time, refusing to accept the relevance of caches. And C programmers who don't know that avoiding conditional branches can dramatically improve performance by avoiding branch-prediction failures.

The argument about not knowing how the machine works is as old as high level languages - perhaps even as old as assembler, which makes it less obvious how many machine words each instruction takes, which is relevant to performance.

But C abstracts away key features of the machine just as much as C++ does. C doesn't tell you about the layers of cache, or virtual memory. And C, like C++, doesn't guarantee to respect your understanding of the machine.

IIRC Torvalds has run into undefined behaviour issues WRT bit-fields in C recently, causing memory corruption in the Linux kernel. I've personally run into undefined behaviour issues WRT pointer arithmetic, overflow in signed integers and more. That's equally undefined behaviour whether you're writing in C and C++. We all know that our machines use 2s complement for signed arithmetic, but the standards allows otherwise, and optimisers are increasingly exploiting undefinedness for performance.

I personally often cannot see where the performance gain is supposed to come from, except in that it breaks code that otherwise worked. But I'm not an expert in compiler optimisation, I guess. Irrespective - the fact is that low-level knowledge of the machine (as opposed to what the standard defines) is increasingly counter-productive these days, leading to bugs rather than performance improvements.

It's a sad fact that in this age, many programmers don't know how to fiddle their bits, other than combining a few flags according to a cut-and-paste formula from an API spec. But the fact is that most programmers don't need this knowledge. Most programmers aren't writing the internals of operating systems. That doesn't imply they're bad programmers. It just means they focus on what is relevant to getting their job done - and learn to bit-fiddle only when they need to. It's really no different to the fact that I no longer know how to combine high-level code with hand-written assembler - I haven't needed to do that for somewhere between 10 and 15 years, so why would I know how?

10. On Wednesday, March 7 2012, 20:43 by IUseToProgramInAssembly

In conclusion you don't like C++ because there are more bad programmers using C++ than in C and you are ignoring all the benefits C++ has over C. WTF!?!

11. On Monday, April 15 2013, 13:15 by terry

There are no reason to make c++ must be slower than c, but there are a lot of incompetent c++ programmers make c++ become slower, not the fault of c++ but those programmers.

I would never choose c over c++,because

1 : no RAII, I hate those ugly goto in my codes,most of them could be killed when we have a better tool like RAII.Why should we always do bookeeping about which memory should be free?Just use RAII to eliminate redundancy codes and eliminate the possible of memory leak.

2 : no template, if you want generic codes in C, there are only two choices, horrible nontype safe void* and extremely ugly macros, not to mention even you
apply the most sophisticated skills of c, the c codes can never be as generic as c++.

3 : just like the community of gcc stated, c++ could help you write better codes, and it could be as fast as c.c++ offers me more choices, when I don't need those features which would or may bring me runtime impact, I have the choice to avoid them, I can't do this with c, no class, no template, no exception, no metaprogramming, no lambda, no auto, no range based for loop, no stl, no thread support.

Last, don't blame c++ if you are a bad c++ programmers who cannot write efficient c++ codes, blame your own incompetent rather than the language.