C and C++ - overview of the most popular programming languages #2

Posted: 2022-12-29
Written by: Tomasz Przedziński

When this article was written, we didn’t know yet that in January 2023 TIOBE will choose C++ as the language of the year 2022! We had a nose! 🙂
In part one of this series on the most popular programming languages, we focused on the C# language. It ranked fifth in the July TIOBE 2022 index. Four months have passed, and the top five positions of the index remain unchanged – no surprises there; each language has retained its podium position.1

 

We create custom software: mobile apps, web applications, and embedded systems. Check out why it’s worth working with us!

 

C and C++ – two separate worlds

Today we will focus on two more items from the bottom of TIOBE’s distinguished top five, excluding Java2This article covers the major differences between C and C++, the two languages that have topped the index since 2001.

It is unfortunately common for candidates we encounter during interviews to combine these two languages under one heading – C/C++. Since I verify technology knowledge during meetings, this signals that the candidate may not fully grasp the differences between these languages – I tend to suspect that it is only about C++. Hence I check it every time. It wouldn’t make sense for a C programmer to group that language with its successor3. As a matter of fact, they differ both in the way they work as well as in the projects in which we use them.

 

The C language: performance, freedom, accountability

C is the most distinctive language of the five mentioned above. It is the only low-level language that significantly affects the areas in which it has practical applications. Although it is a procedural programming language, it can also be programmed functionally or using Object-Oriented Programming (OOP) practices. Keep in mind, though, that the programming language itself does not contain grammatical constructs to help you do so.

Kod C++

“Hello world!” program in C

The C language is typically used whenever performance is at stake. It does not contain mechanisms such as polymorphism, memory management, memory access protection, or type control4. The absence of these mechanisms speeds up the program considerably but simultaneously places a lot of responsibility on the programmer – they have to ensure everything is under control.

The second major factor favoring this language is direct access to resources. The lack of mechanisms that limit the programmer’s freedom means that, in C, it is possible to easily and efficiently manipulate flags in processor registers, make direct entries into various areas of memory or manipulate communication with the components of the device on which we are working.

This freedom and efficiency are why drivers, operating system kernels, web browser kernels, the foundations of databases, graphical interfaces or game engines, and the vast majority of embedded software are written in C. What’s more, a C programming language is often used to write the lowest layers of software.

The grammar of the C language is very simple. It is pretty easy to write code in C that carries out complex mechanics, and that will compile without errors – and even if they occur, they are usually fixable in a few moments. Unfortunately, the apparent simplicity also means that few things are guaranteed in advance or checked at the compilation stage – it is very easy to make hard-to-find errors in this language that are usually unthinkable in modern programming languages. Moving from a high-level language to C requires you to discard many existing habits and build many new ones.

Kod języka C

An example of a typical problem when programming in C. Pointers can be arbitrarily cast (converted to another type), and generic functions cannot be safely created. It is entirely up to the programmer to ensure the correctness of these solutions.

Kod języka C++

The use of pointers to functions is particularly dangerous in C language. Passing the wrong pointer – which often happens by accident in complex projects – can lead to unexpected results. When this occurs, the compiler warns but does not throw an error. Furthermore, many programmers ignore such warnings, believing they are about something else.

Although C’s syntax can be learned quickly, it is one of the most challenging programming languages to work with in the real world. Freedom of implementation makes it difficult to standardize a particular approach to solving problems. In projects written in C, programmers repeatedly reinvent the wheel5, contrary to the practice of code reuse. Such a wheel always looks slightly different from the previous one and requires separate tests. If several teams are working on a single product, this is very likely to happen6.

On top of that, having a magic trick in the form of macros, through which code can be generated on the spot, allows complex and difficult-to-understand codes to be easily created. Hence, it is not surprising that one team may not want to use the code of another collaborating team.

It is worth noting that much of the code written in programming language C, due to optimization and adaptation to a designated platform, is only intended for one specific application. However, proper separation into layers and components could make some of this code reusable. It is an approach I have used many times in the projects I’ve worked on. Sadly, this approach is uncommon in my experience.

When working at the lowest layer of software, any bug can turn out to be critical. In addition, a single hole may have global consequences when it comes to security issues7.

In embedded software, on the other hand, where updates are distributed over hundreds of thousands or millions of pieces of hardware, one critical error can even cause devices to stop working altogether8. It is no secret that anyone who has implemented Over-The-Air Update (OTAU) at least once is familiar with the vast number of factors involved, the delicate nature of the process, and the ease of making mistakes. It is pretty easy to overlook a mistake or forget a scenario when setting up an OTAU system. This is because several safeguards have to be implemented, and critical scenarios have to be handled complexly. In some cases, this mistake may result in permanent damage to the equipment, which then requires repair or replacement.

It is somewhat of a paradox that such a dangerous language as C is being used for such critical tasks. I am not surprised, however, by the growing popularity of Rust9 among programmers, whose design seems to be the inverse of the C language grammar – it severely restricts the programmer’s freedom, protecting code from many critical problems while giving almost identical performance and low-level component and memory manipulation as C.

By far, C is the most brutal language I’ve had to work with, not because of the complexity of the language itself, but because of the problems that have to be solved with it and the need to protect large amounts of code from many errors manually. C language is a very basic tool, meaning it is tough to solve complex problems with it. The most significant difficulty in mastering it lies in creating safe solutions and avoiding errors. It requires a great deal of project discipline and careful analysis of the code, both your own and that of other team members.

 

C++ Language

C++ differs greatly from its predecessor in terms of its features. C++ is a high-level object-oriented language, although it allows low-level access to resources just like the C language. C++’s historical baggage, in the form of constructs from C, is both its strength and one of its biggest problems10. Nevertheless, this does not mean that one should write in C++ language in the same way that one writes in C. Quite the contrary, in fact.
Kod C++

“Hello world!” program in C++.

Libraries such as standard C++ and STL (Standard Template Library) provide a wealth of ready-made solutions, which in C had to be created from scratch. STL-related questions are frequently asked at job interviews for a reason – a thorough knowledge of the STL allows many basic problems to be solved quickly and efficiently. It addresses, among other things, memory management problems.

Since the entry of the C++11 standard, there has already been an increasing move away from the use of ‘pure pointers’ (raw pointers) from C, in favor of a range of available memory management mechanisms such as smart pointers. In terms of reflection, C++ still limps behind its newer rivals, but the most useful mechanisms, such as RTTI11 (run-time type information) and type traits, are there. On top of that, it handles exceptions12.

C++ programming language, like C, is used wherever performance counts because most mechanisms that slow it down are optional13. Hence, you can enjoy its many facilities with relatively low or no additional overhead. Thus, its applications are largely overlapping with those of C. Many new projects (including embedded systems) touching the lowest layer of hardware use both C and C++. It is popular specifically in embedded Linux devices.

The capabilities of this language allow it to be applied effectively in almost any field. It is possible to successfully develop desktop applications using platforms such as Qt. Modern C++ has a number of asynchronous programming mechanisms, including the task creation mechanisms found in the Event-based Asynchronous Pattern (EAP) or the mechanisms implementing the concepts of ‘future’ and ‘promise’ used in the Task-based Asynchronous Pattern (TAP). It is also widely used as a backend for many web services. C++ is a truly versatile language that will work well in any role but is obviously not the ideal choice for every project.

A significant issue when working on projects written in C++ language is managing the compilation process and additional packages. These are problems that are virtually non-existent in Python and marginally relevant in C#. Several external tools can be used to manage compilation – depending on what year the project was written; these can be Make, Autotools, or, more commonly nowadays, CMake. On the other hand, projects created in Visual Studio are usually compiled by the tools provided by the IDE. Managing external libraries requires using a package manager such as Conan14, which pairs well with the CMake tool. However, these are not tools that work reliably and give the same result in every project or on every platform. Working with them requires a bit of familiarity and experience, especially if you want to create your own package for use in other projects, whether proprietary or open-source. When working with code in C++ programming language, the compilation process cannot be disregarded. It even needs to be given quite a bit of attention, especially at key moments in the development of the project. This is especially true when extending the project with new modules, adding dependencies, implementing or changing the CI process, etc.

Despite the many improvements that help programmers in their work, C++ remains a difficult language. Its grammatical constructs often seem unnatural and require knowledge of the intricate mechanisms that run in the background to make the most of its power15.

Incomprehensible compilation errors, taking up tens of pages of text, are commonly encountered when working with templates. The example below is one of the simplest. In complex projects, looking for the correct error message takes a long time, and the IDE cannot always help with this.

Kod C++
Kod C++

This example compiled by gcc 9.3 generates 60 lines of error. The last message (given below the code) can completely confuse a novice programmer because it is in no way related to the actual error in the code. C++ programmers quickly learn to scroll through the error logs to the beginning, because usually, the first message contains the correct error information (in this case: a typo on line 7 of the code).

However, compilation errors are the easiest to deal with, as they immediately come to the surface. Much trickier errors are those that, for instance, exclude RVO (Return-Value Optimization), ignore or incorrectly use move semantics, or one of the many advanced optimization mechanisms available in C++ language. It takes a lot of experience to recognize that a mechanism is not working correctly and to figure out why it is not performing. Moreover, the multitude of hidden mechanisms that occur “in the background” in various situations without informing the programmer makes it significantly more difficult for beginners to work with this language effectively. This is a programming language in which I have more than a decade of experience and yet, I have been frustrated more than once by its problems. Undeniably, C++ has the highest entry threshold of all programming languages nowadays, and requires years of experience to use it skilfully. As each updated edition16 introduces novel mechanisms that enrich the language, things are not made easier. These mechanisms often change the approach to certain concepts and require getting used to. For this reason, many projects choose to stop at a particular version of the C++ standard and not upgrade to a newer one17 – this is also the reason why it is so crucial to distinguish which version of the language a given programmer has only had contact with and which they have spent more time with.

 

Long-term stability

The C and C++ languages, despite the enormity of their drawbacks, need not fear that they will lose their popularity in times to come – too much code has been written in them and still needs to be maintained. There are also new products being developed all the time for which they are best suited. Nevertheless, nowadays C and C++ are rarely chosen by novice programmers. This is simply because there are interesting job opportunities in competing languages that are much easier to learn and work effectively in less time. And yet, the popularity of these languages is constant, and so is the demand for C and C++ specialists. There will be no shortage of jobs for those who are not afraid of the challenges of learning these languages.

We have already covered C#, C++, and C. We will conclude the series with Python, a language that has little in common with the above three. However, its popularity has quickly pushed it to the top of the index. In 2018, it occupied only 3% of the market. Currently, it is around 17% – what makes it such a popular choice among programmers? The third part of the series will reveal all.

Let’s talk about your project! Our engineers will offer you the most suitable technical solutions to bring out the potential of your idea. Check out the projects we have completed for clients who needed custom software.

If you have a roadmap and description of the application ready, we can prepare a quote within 24 hours after receiving the specs. If not, no worries. Get in touch with us and set up a free consultation to work together on the next steps.

1 – The first four, in the order of Python, C, Java, and C++, occupy 55% of the index. This leaves the competition far behind, and their places at the top seem unthreatened. Their positions have increased by a combined total of almost 7% since July. In contrast, C#, which was the focus of the first part of this series, has maintained its fifth position on the list, despite losing share over the period. As I mentioned in the previous text, this programming language is no stranger to jumping in popularity.
2 – As we mentioned, Java is omitted from this series since its author lacks practical experience and is not competent to discuss its advantages and disadvantages.
3 – C++ is often treated as an ‘extension of C.’ Currently, this statement is very misleading. The C++11 standard has introduced several mechanisms that bring C++ much closer to modern languages such as Java and C#. There is a reason why versions of C++11 and later are called ‘modern C++’, separating them with a thick line from ‘old C++.’ It is no longer appropriate to think of C and C++ as similar languages.
4 – C is notoriously difficult to use when manipulating pointers to arrays or data structures. Passed pointers can refer to a memory area of any size and type. In complex projects, where functions accept generic pointers, the programmer himself has to create a mechanism to inform which memory area is currently being dealt with or trust that the functions in question always operate only on the designated data types.
5 – For example, almost every embedded systems project will sooner or later need a cyclic buffer. Hundreds of libraries on the market offer such a data structure, but I have not encountered a single project that would use such a library. It is constantly rewritten. Of course, we are talking about a straightforward but typical example. There are many more such examples.
6 – This is mainly due to teams preferring to change an existing implementation or rewrite it rather than jointly developing a version that two or more projects could use. Sometimes this results from a need for optimization, and sometimes it is an attempt at premature and unjustified optimization.
7 – For instance, when a massive vulnerability in the OpenSSL library, called Heartblead, came to light, it was estimated that approximately 17% of servers with CA (Certification Authority) certificates were vulnerable to an attack that made it possible, among other things, to find out the server’s private keys, eavesdrop on the entire connection to the server or perform a man-in-the-middle attack. Websites, applications, and browser add-ons analyzing whether the servers we connect to are vulnerable to this vulnerability started to appear like mushrooms. The security of every internet user was at stake. Since then, several new vulnerabilities have been discovered in the OpenSSL library, with the highest possible threat rating of 10. Recent vulnerabilities of this type date back to July 1 of this year.
8 – Released in March 2022, the macOS update to version 12.3 caused a huge number of devices with a changed motherboard to stop booting. The workaround for this problem was a labor-intensive procedure to get devices functioning again.
9 – For the seventh year in a row, Rust has been recognized by StackOverflow users as the ‘most loved language. This is the language in which the largest number of respondents, 87%, want to continue working. Recently, Linus Torvalds, the lead author of the Linux kernel, stated that by 2023 at the latest, the first modules written in Rust should be in the Linux kernel. Google, in turn, has started rewriting parts of the Android operating system into Rust. In October, Rust once again jumped into the top 20 languages in the TIOBE index. Its popularity continues to grow, showing that developers working at the lowest layer of software see the need for and appreciate the existence of a language that imposes a set of very rigid rules, but gives in return security and much more control over the operation of the program.
10 – There are still macros in C++, although most of the uses of these macros can be replaced by templates and the updated syntax introduced in C++20. You can still write procedural code in C++, although you will not utilize most of the capabilities of the language. You can manage memory in C++ as in C, although there are several added mechanisms to make this work much more manageable. You can also do what you like with pointers to areas of memory. However, again, C++ adds a mass of mechanisms to guard against basic errors arising from the improper use of pointers. Technically, code written in C with very few exceptions can be compiled with a C++ code compiler. But, of course, not the other way around.
11 – Interestingly, there is a deep awareness of the cost in terms of reduced performance that the RTTI mechanism introduces into a project. This is why RTTI can be disabled in many C++ compilers, preventing its use in a project.
12 – As with RTTI, the overhead associated with throwing exceptions is so high that it is often considered bad practice to use exceptions in C++. Compilers also usually allow their use to be blocked. In my opinion, the ‘bad karma’ about exceptions is not so much about the cost of using them but about the inexperience of programmers in using them and the cost of using them incorrectly. These mechanisms are crucial in all high-level programming languages for a reason.
13 – Leaving aside the RTTI or exception-handling mechanisms mentioned earlier, which can be turned off, nothing forces the C++ programmer to use inheritance and polymorphism. In C++ classess are allowed to inherit from multiple other classes, which is a very slow mechanism when the base classes are not purely abstract. However, no one requires that this mechanism be used, and it is even widely regarded as unsound practice.
14 – Paradoxically, installed through Python package manager.
15 – For instance, although many programmers understand what the SFINAE (Substitution Failure Is Not An Error) concept endemic to C++ is all about, programmers who are able to correctly describe all the steps involved in overload resolution, let alone apply this knowledge, are worth their salt.
16 – The current leading version is C++20. Work on C++23, which according to the notation would be published in 2023, is still ongoing.
17 – In embedded systems environments, C++11 is the most popular standard, although C++14 is slowly making its way in. In the automotive industry, C++14 is widely used. It is also the most commonly used version in projects on the market today. C++17 is gradually becoming noticed in existing projects. The newer they are, the more their elements are written in C++17. In contrast, I have only encountered C++20 in job offers for new positions. I don’t see its widespread use in existing projects yet.

 

Written by: Tomasz Przedziński,
Software Architect

Born in ‘86, graduated with a master’s degree in Applied Computer Science from Jagiellonian University in Krakow. He received his Ph.D. in Software Engineering from the AGH University of Science and Technology. He specializes in C++ but has developed a strong liking for embedded environments. His fondness for algorithmics and solution optimization is no secret. Quality, building advanced test environments, or extending existing ones in his professional work are his main focuses.

CONTACT US
Complete
the form below.
We will contact you to set up
a conversation at the convenient
moment for you.