DEV Community


Posted on

C Development is a Mess

Hey folks, it's been a while since I posted here! I've been quite busy with life, especially working at a few companies such as Hyperbeam, DigitalOcean, and a few others. That being said though, let's get into the real meat of today's topic. Why is C/C++'s development such a mess?

Build Systems

One of the biggest pain points of C/C++ development in my opinion is the build systems, there is many, many build systems, a lot of them not being compatible with the other. We have CMake, Premake, Xmake, MSBuild, Bazel, and many others. This has practically resulted in that popular XKCD comic about standards where they just add to the mess.

XKCD comic about standards

This is not the way to handle things, creating yet another build system or project configuration system will only result in yet another attempt at improving the situation, at the cost of making it worse instead. What actually needs to happen is for there to be one, uniform build system. Let's take a look at other languages: Rust has Cargo, Zig has it's own built-in build system, Haskell has stack, Hell, even Fortran which is older than C/C++ has it's own known as FPM! My point being creating more is doing less.


Another problem is that C, but especially C++ has feature creep. Now this is quite normal in the C/C++ ecosystem. But this has resulted in compilers not being able to fully implement standards. By the time a compiler catches up to the current standard, a new standard is already out. This is why a fair amount of people tend to prefer to stick to one standard and not move to newer ones, because it's consistent.

The point of a standard should not be to expand the language or tool, but rather to, as the word implies, set a standard in which compilers, and related tools can stick to and follow a written down point of truth. This is not entirely possible when every time that point of truth has been followed, a new one is made which may completely eradicate any previous truths.

Compiler Compatibility

This might be a hot take, but personally I hate compiler specific extensions such as GCC's C Extensions. The reason for this being it causes your code base to only be compatible with that specific compiler, and no others. This means for anyone using Clang, TCC, MSVC, or other C toolchains, they are shit out of luck unless they install GCC. I shouldn't need to install X specific compiler for the language I'm compiling when I already have a compiler for that language installed on my system. If the extensions were shared across compilers and toolchains however, that would be great!

Package Management

I touched on this in the first section about Build Systems, but we're going to dig into this a bit more as it's always been a bit of a sore spot for me personally. Package management in C, in all honesty, sucks. There is no central place to search (not get) packages, due to the build system mess, you run into packages using X build system which you may not be using so you have to port their build script to your build system, package management is inconsistent across platforms (although microsoft's vcpkg attempts to fix this), and there's many more issues that you have to deal with due to inconsistencies, too many options, and other issues that affect it.

Final Words

What do you think? Do you think C/C++ development could be improved, if so what is your opinion on how it could be improved? Reminder to stay chill, don't attack people for giving lighthearted opinions. This is my opinion as someone who has done C/C++ development for years, and obviously doesn't reflect the opinion of others.

Top comments (14)

phlash profile image
Phil Ashby • Edited

Hi Hanna, I'll bite! Here are some thoughts from someone who grew up with C, then C++, and has coded regularly in both for 40 years (yes, me: I'm old!)...

  • It's an Apples and Oranges situation if you look to way more modern ecosystems such as Rust, Go, etc. then expect C/C++ (language definitions only, constrained by committee rules not to go elsewhere) to have a similar ecosystem defined by the language designers.
  • C (and to a lesser extent C++) evolved with computers and operating systems, thus the language(s) carry a lot of baggage from that history, including the Unix philosophy to do one thing well - compile portable human-readable software to many, many targets. This does not include surrounding tools and techniques such as package management (non-existent when C was created) or build tools (although make appeared around the same time and I still use it in preference to more 'modern' solutions when I can - it Just Works).
  • Change happens. Standard versions exist as markers along the way to help implementers and users target their efforts. Rust has changed over time, Go, Python, C#, Java, etc. etc. all have a major version history that reflects significant adjustments to the language design. Many of these languages retain backward compatibility (but not all have done so over major changes - Python for example). C/C++ are old and have seen lots of change, most of it very carefully arranged to remain compatible with Very Old Software (tm) - like your O/S 😁 I think the committee do a good job here (some would disagree.. YMMV).
  • Tooling choice means I (the dev) can choose to use a common build system for multiple languages / targets (eg: make, CMake, Bazel, etc.) whereas a fixed ecosystem forces me to learn a new build tool, and make a larger commitment to the language, a form of lock-in that should be a conscious choice by developers..

As a summary: the C/C++ language does not define an ecosystem, to avoid lock-in and maximize portability of the language and compiler. Ecosystem choice depends on the target(s) and should be a conscious choice for developers to pick something appropriate to their needs. Standards are waypoints in the continuous change of life, and a Good Thing to help others achieve Useful Goals.

novicedev7291 profile image
Kuldeep Yadav

@phlash What would you advise programmers coming from Js, Rust, Java, C# land? As I guess their first expectation to get started with little complex project and the biggest obstacle is that this too much flexibility, I guess since the community is very old and mature, there must be some lesson learnt and comparisons made about tooling.

phlash profile image
Phil Ashby

Good question..

  • Js (ECMA-262) is similar to C/C++ in that it's a language specification only, not an ecosystem, so developers have to choose other tooling themselves (eg: Webpack, Node.js, NPM, etc.), often this is provided as part of a larger framework like Electron that devs have not looked beyond..
  • Rust has cargo for build and package management, it's explicitly separated from the host OS (no use of packages that may be in my Debian repo for example) and by default builds static executables that have minimal dependencies, so devs from this world have not experienced dependency hell :)
  • Java is another language specification only (almost as old as C++), tooling is varied and package management is external to the language. Devs from this world have more structured expectations however due to popular repos like Maven central and tooling that knows how to use it (eg: Ant, mvn, gradle)
  • C# (.NET in general) provides a runtime specification (ECMA-335), compilers for multiple languages that target it and build + package management tooling (dotnet) backed by a well-known repository (NuGet). Devs from this world are in a similar position to those familiar with Rust.

I would add that the target platform can significantly affect choices of tooling (eg: microcontrollers such as Arduino may only have one option, such as the Arduino IDE).

Microsoft and Apple have put considerable effort into creating IDEs (VisualStudio, XCode) that remove sticking points to C/C++ development for their platforms, however this comes at a price - there is a lot going on within their tooling that could bite you if it fails. If you are developing a 'normal application' for Windows or MacOS though - these tools are the easiest way to get going with their project setup wizards etc.

Existing projects will come with tooling choices already made (often CMake, although Bazel is flavour-du-jour at Google), so as a new dev you would have to learn how those tools work anyway..

New projects, as a new dev perhaps working through Advent of Code puzzles or similar - I would recommend keeping things simple: only using packages from your operating system repository (on Windows: Winget or MSYS can help), plus a simple Makefile command-line build. This way you get going with minimal pain. Later on, perhaps look at CMake if you need multiple target support, then select an IDE (I use VS Code with a C/C++ language server), which often makes the next point easier:

Learn to use your debugger! GDB is a good command-line debugger, but very terse. That said, often the only commands you need are bt and list 😁. A good IDE will come with a debugger or connector for one (VS Code for example has a GDB connector), so you get to poke about in a GUI and see more stuff.


Thread Thread
novicedev7291 profile image
Kuldeep Yadav

Thank you so much @phlash and you are right, having worked with Java for so many years, I had the same expectations 😁...I have written c/c++ in school & college but after that worked on java only..... recently I started exploring internals of OS and other stuff and c/c++ everywhere.

hanna profile image

That's a good point of view!

darkwiiplayer profile image
𒎏Wii 🏳️‍⚧️

It seems a lot more puzzling why C is so messy if you primarily think about desktop and server computing, but when you start to consider the multitude of other devices that C has become a sort of programming lingua franca for, it does make some more sense.

Once you consider how many devices C targets, it's hard to imagine what a universal build system or package manager would even look like.

Admittedly, C++ probably doesn't have the same excuse, at least not to the same degree, but I don't personally see the need to defend that language in any way whatsoever, so I'm happy to just file that as another instance of "C++ does things C does without wondering why C does them in the first place"

pinotattari profile image
Riccardo Bernardini

One serious problem I remember from my C days is the "flexibility" that the standard allows to some parts of the language.

For example, I am thinking about the integer types char, int, long int, and so on... If I remember correctly, the standard dictates that char is 8 bit wide, but about the other types it just dictates that int is not smaller than char, long int is not smaller (note: not larger) than int and so on. We only know that int is the integer type that the most efficient integer type. On some small CPU int could be 8 bit long, while on other CPUs it can be 32 bit long. This is a problem when you need a specific integer length. Sure, there is inttypes.h, but in my experience not every system has that, I remember a C compiler for a DSP Texas that had not inttypes.h, forcing me to provide one ad hoc that, however, needed to be hide when working on my desktop (I developed the program on the desktop and then moved that to the DSP)

cubiclesocial profile image

In my experience, there are two major problems with C. The first is that there are performance optimizations that can't be easily done in C. An example of this is C++ templates. With C++ templates, generation of complex algorithms can be up to 50% faster (e.g. template sort vs qsort()). This is because the compiler generates new code at compile time and can optimize the AST for the specific scenario.

The second is that the C macro preprocessor is terrible. It is quite inflexible and features like token pasting create a huge mess.

Importing libraries in C/C++ definitely has significant challenges. Part of the problem is that each compiler environment presents its own unique approach to compiling and linking code. There are a ton of different C/C++ compilers out there that all have different levels of standards conformance and compatibility. C has always straddled the line between assembly language for a specific architecture, system API calls, and portable, cross-platform code. Compiling code for one compiler might not compile for other compilers even on the same platform. Using the macro preprocessor to solve problems instead of fixing the compilers is largely how the situation with libraries got to its current state. The standards committees also, for decades, made the assumption that the device being output to was a line printer instead of modern technology. Multithreading was not a thing for many, many years despite having decades of multitasking OSes. The C standards committee is exceptionally slow to adopt newer ideas and technology out of fear of making another mistake like gets().

As to build systems, I'm not a fan of any of the existing build systems out there. I don't write much C/C++ these days but when I do, I end up writing tiny batch files/shell scripts that invoke the compiler/linker directly. I think the best applications are those that stick to a handful of files for all of the code instead of behemoth projects with thousands of files that have to be scoured with findstr/grep just to figure out which file to edit. When there are only a handful of files to compile, simple batch files/shell scripts work fine instead of dragging in a whole third-party build system just to build the software.

michaeltharrington profile image
Michael Tharrington

Woot! Been a while since I've seen ya around DEV, Hanna. So good to see a post pop up from ya!

hanna profile image

Sure has! Been rather busy and things.

michaeltharrington profile image
Michael Tharrington • Edited

Oh totally understand! Life takes hold and time flies by.

catsarebetter profile image
Hide Shidara

C development is completely pointless imo, super hard to code in.

Could be wrong though, I said the same thing about COBOL and Fortran and a scary amount of our financial system are built on those.

hanna profile image

Fortran still has a thriving community.