(Back to index)


Why I hate C

Let's admit it: I love C++. I know its flaws and deficiencies, and I know what it is and isn't good for, I readily admit that it's not a good programming language for a beginner to start with, but I still like the language. Most of its flaws and ways to shoot yourself in the foot don't affect me because I know them and I know how to write programs so that I'm completely unaffected by those flaws. I like the tools C++ offers for both abstraction and optimization at the same time, which is why you can create very efficient programs which are still very well designed and abstract, making the program easy to modify and reuse.

Most people hate C++. Basically all people who use higher-level languages hate C++ (some of them for informed reasons, most of them because of FUD). However, more specially, C-hackers hate C++, and about 100% of them for the wrong misinformed reasons. They often consider C a superior language, which is just crazy.

Even though C++ is more or less a superset of C, I still hate the guts of C. It would be a real nightmare if I had to write any but the smallest and simplest programs in C. If I had to make a choice, I would even prefer Java any time rather than C.

Why do I hate C so much?

Wrong kind of simplicity

Many C advocates argue that C is a good language because of its simplicity and "minimalism".

This argument tries to ride on the fact that simpler languages are usually considered better than overly complicated languages. For instance, languages such as Lisp, Haskell or Eiffel are praised for their simplicity, especially when contrasted with more complicated languages such as C++, Java or C#. In this case simplicity of the design of the language directly correlates to how much the programmer needs to know about the language in order to write complex programs, and how much work he has to put into writing those programs.

C advocates try to ride on this concept by equating the length of the language definition and its standard with the concept of "simplicity". However, the length of the language definition does not necessarily correlate with the simplicity of the language, and C is a perfect example of this.

With C, "simplicity" becomes a limiting factor rather than a beneficial one. In other words, the "simplicity" in C doesn't help the programmer write programs, but on the contrary limits what the programmer can do. In other words, C is too simple in that it doesn't offer the tools necessary to write simple programs.

The end result of this is that programs written in C become a lot more complex and lengthy than programs in truly simple languages. Simplicity cannot be measured by the length of the language definition, but by the length and complexity of the programs written in that language. And C fails miserably in this regard. Even simple tasks, especially those which require dynamic memory handling, are often very complicated to implement in C.

Almost everything, especially things which require complicated dynamic data containers, basically needs to be written from scratch in C. For this reason many C-hackers have their own collection of non-standard libraries which they use to supplement what standard C doesn't offer them. Of course if they need to implement a program in an environment where they don't have that collection of libraries, they will need to again implement everything from scratch, starting from the most basic tools.

Even with that collection of non-standard libraries, the libraries themselves are often quite hard and complicated to use, and usually rather unsafe and easy to use wrongly. That's because C, in its "simplicity", doesn't offer any tools for safety (which is an issue I discuss in more detail in its own section below).

I readily admit that C++ is a complex language, and in no way in par with the best "simple" languages in this regard. However, at least C++ offers many tools which aid in writing much simpler and safer programs more easily (while still maintaining efficiency) than C. A typical small program which handles dynamic memory (eg. by reading data from a file an processing it in some way) will be much shorter and simpler in C++ than in C. The complexity of the C++ language definition actually helps in writing shorter and simpler programs, as ironic as that might sound.

Poor tools for abstraction

Abstraction is the quintessential key for large programs. The larger the program becomes, the more important the role of abstraction. If the program doesn't use enough abstraction, in other words, it uses "spaghetti code", it becomes very hard to understand, expand, modify and maintain. Also reusability of the code gets seriously diminished if not outright removed.

It is possible to write abstract programs in C, to an extent. However, the language offers only few and rather poor tools for this.

Many C libraries and larger programs use ugly hacks to get around the limited abstraction tools. These hacks include things like "opaque" pointers (which opaqueness is not really enforced by the language) which are both complicated to use and unsafe. They also often result in less efficient code (both in speed and memory usage). Void pointers are often used as these "opaque" pointers, which just beg to be used erroneously because the language offers zero guarantees or protection with regard to them.

The only more or less "abstract" type C offers is the struct construct. Of course everything inside a struct is wide open to any code to access, so there isn't really any abstraction involved. It's simply a conglomeration of basic types put together. The language offers no tools to regulate access rights.

It's also hard to write code which would work for more than one type. Often attempts at this produce rather inefficient code, which is needlessly complicated to use. (The quintessential example would be qsort() of the C standard library. Contrast with std::sort() in C++.)

Usually the need to handle more than one type results in code repetition in C, which is one of the biggest sins in programming.

Poor tools for safety

Safe code is good code. If something is hard to use in the wrong way accidentally, that's good code. If that something has the necessary tools to enforce safety and to automatically clean up after itself, that's a good thing.

C offers little to no tools for this. This is one of the major reasons for buggy programs out there which eg. leak memory, crash or have exploitable security holes. It's just too easy to accidentally write buggy code because there are no safety tools provided by the language to minimize the risks.

Just as an example, consider the "strings" in C: There is no way to easily write code handling dynamic strings in C which would make it hard to accidentally write buggy code. It's very easy to, for example, leak allocated strings from functions with multiple exit points. That's because the language offers zero protection against this. There is no way to make the string automatically deallocate itself when the function is exited, nor even to automatically detect (at either compile time or runtime) that the string was leaked.

Code which handles multitudes of dynamic strings necessarily becomes very complicated. The more the code is expanded and developed, the more complicated it becomes in this regard, and the risk of accidental errors grows accordingly. This can be somewhat avoided with certain programming practices, but it's purely up to them because the language itself offers no tools to enforce or check anything, or to do anything automatically.

It also becomes complicated to enforce string boundaries, ie. to avoid accesses outside of the string.

C++ offers many tools for safety and automatization. It's possible to create classes which are hard to misuse accidentally (of course they can always be abused, but it would have to be very deliberate, and nobody would do it by accident). Strings can clean up after themselves automatically so memory leaks are avoided. Access inside the boundaries of the string can also be enforced if necessary.

No RAII

One of the main reasons why C has so poor tools for safety is because it completely lacks support for the RAII idiom, which is an integral part of C++.

I'm not going to explain in detail what RAII means here, but in short it refers to the concept of scope-bound automatic lifetime of objects and the resources they manage. (The idiom is realized by the compiler-imposed constructors, destructors and assignment operators, and the binding of local object lifetimes to the scope they reside in.)

This is a comment I read somewhere in a blog comment:

A lot of the most readable, maintainable system-level code I've seen is "object oriented C" - C with a set of macros/libraries/conventions that implement a much simpler kind of OOP than C++ without encouraging the bad habits that C++ practically makes inevitable.

Well, sorry to break this to you, but it doesn't matter how many "macros/libraries/conventions" you develop for "object oriented C", it will never reach the usability and safety of C++, for one simple reason: C has no support for RAII. For this reason it doesn't matter how much you struggle, how many clever macros, libraries and conventions you develop, your code will still be uglier, more complicated, harder to write and less safe than the equivalent C++ code.

(I find it amusing, and kind of hypocritical, how he specifically mentions coding conventions as one of the main tools for creating readable and maintainable C code, as if similar coding conventions couldn't be used in C++ as well. In fact, the equivalent C++ code would require significantly less coding conventions for the same degree of readability and maintainability, resulting in simpler code, and one of the main reasons for this is, once again, RAII.)

RAII makes writing simpler and safer code a lot easier in C++ because lots of the tedious "coding conventions" can be automatized by the compiler, so the programmer doesn't need to bother (which both simplifies the code and lessens the probability of mistakes). And no, this is not done at the expense of efficiency.

Poor standard library

The C standard library offers very little tools for anything besides some basic (and often unsafe) I/O and some completely unsafe (and often inefficient) string manipulation functions. It offers no data containers nor algorithms (except for the horrible qsort()) which could be useful for the programmer for many tasks.

Again, this is a perfect example where "simplicity" becomes a limiting factor, as it forces programmers to do most things from scratch, even for the most basic tasks.

For example, give this type of task to a C programmer: "I need a data structure containing strings of variable length. The amount of elements to be added to it is in the few millions, depending on the input. It must be fast to add and remove elements from it, as well as searching for existing elements." Then give some example situation where that data structure should be used, for the C programmer to implement. Of course demand the code to be fully tested and debugged.

The C programmer will spend the next few days implementing and debugging such data structure, and it might or might not be bug-free.

A C++ programmer needs to do nothing (besides implementing the example program which uses the data structure). That's because the standard C++ library already offers such a data structure.

After the task is done, tell to the C programmer: "Oh, and by the way, I need a similar data structure, but which supports structs which have a few elements of type double in them."

The C programmer will then spend the next day or two implementing the support. The C++ programmer doesn't need to do anything. The support is already there.

C just doesn't offer anything, and everything has to be implemented from scratch. This makes even the simplest tasks really laborious to implement.

Of course C++ doesn't offer everything either, but at least it offers a lot more useful stuff than C does, stuff which is very often needed in practical programs. Also, the stuff the standard C++ library offers is usually much easier to use safely than anything C offers or could offer.

About the productivity of C vs. C++

This is an example of a very typical opinion about C vs. C++, which can be found at a website promoting an object-oriented extension to C:

C++ is a large and complex language from a syntactical standpoint, especially when compared to the minimalist C language. There are two problems associated with this complexity. The first is that the learning curve associated with going from a proficient C programmer to a proficient C++ programmer is at least six months for most programmers. The second problem is that code written in C++ tends to be more complex and therefore harder for new programmers to understand and maintain. The bottom line is that C++ development tends to be much more expensive than C and employers end up being much more dependent on a few "experts".

Basically everything in that paragraph is pure bullshit.

In my opinion the situation is the complete reverse of what these prejudiced C hackers claim. While it's possible to create quality C code (to an extent), and there are many competent C programmers out there with lots of true talent, the limitations of the language more or less force the implementation of large complex programs to be complicated, verbose and obscure, with large amounts of hidden coding conventions and metaparadigms (which exist solely because of the limitations of C) which may not be at all obvious nor clear to a third-party studying the code. Extensive ancillary documentation may be necessary for a new programmer to start understanding the subtleties of the program (which, as said, exist there solely because of the limitations of the language).

C++ often allows much simpler approaches for implementing equivalent code, with less hidden coding conventions and metaparadigms.

For example, if I need a fast ordered data container for a couple of thousands of elements, in C++ I can simply go ahead and use a std::set or std::map in a completely simple and straightforward way. I don't need to worry about programming conventions in order to avoid memory leaks nor anything similar which would be necessary in C. The resulting code will be short, simple and easy to understand. Anyone who understands the usage of those standard containers will easily understand the code.

Writing that code, updating it, and a third-party understanding it will all be simpler and faster. The code will also be safer and cleaner without using any special metaparadigms or coding conventions.

Thus it is my strong opinion that trying to implement and maintain a large project in C (instead of C++) is counter-productive and expensive.


(Back to index)