C is alive and well - Embedded.com

C is alive and well


The debate of retiring the C language has been ongoing for years now, but replacing C is unrealistic as it is often the only alternative to any other programming language.

Recently an article titled “Is it time to retire C?” was published here. The article is well versed and its premise is well-argued, yet I believe that the conclusions are misleading. The debate of retiring the C language has been ongoing for years now. Engineering managers argue that using the C language is too demanding and makes any software project a complex and error prone process, while software sales managers complain that it takes ages to create a product to sell.  The opponents often argue that it depends on the job at hand — the ubiquitous “find the best tool for the job” stance and the supporting legacy systems (which are many) are nearly impossible to maintain without C.  My perspective is from a slightly different angle though: is it at all feasible in today’s software industry to retire C? Also, my viewpoint is largely design and development of embedded systems.

It has been half a century since the introduction of C. Prior to C, the art of programming has indeed been “art”. Since the introduction of C, the art has quickly become a true profession. Complex projects have been planned and put together to enhance all aspects of life: software now controls anything from a simple electrical plug to complex spacecraft systems to social network systems. There is no question that all these life changing advances wouldn’t be possible without a simple tool like C.

There are many alternatives that are successfully and rightfully used to support the software ecosystems. So how significant is C in a modern software development toolset? Perhaps it could be replaced with modern alternatives? Let’s consider those alternatives.

Alternatives to C

Alternatives might be “safe” languages such as Java, C# and Go. One of the main problems with these languages is that, by design, they suck system resources like a vacuum cleaner. Safety is achieved primarily by using a garbage collection (GC) process that handles memory allocations safely so that allocated objects never use the content of other objects. In the beginning, GCs had to traverse all objects’ references to make sure that the object in question is out of reach. That would “overheat” the memory bus especially if the number of objects allocated by the application is significant. Over the years, many improvements were made to garbage collectors — background GC, garbage collection through multiple parallel threads, generational collectors, etc., etc. As a result, modern garbage collectors in languages like Java are not extremely expensive from a performance standpoint. Yet, in embedded systems, where memory is scarce, garbage collectors must do the cleanup frequently and often any concomitant overhead is unacceptable.

Another “safe” alternative is Rust. No question about it, the idea of Rust is plain wonderful: “compiled means run”. Yet in practice using Rust is so complicated that it too often feels like “programming for the sake of programming”. Development in Rust is turned into a fight with the language as opposed to solving the pressing matters at hand.

Consider for example Rust’s approach towards working with pointers, a normal programming practice in C. It is so complicated that it is almost useless. Listing 1 illustrates a simple function that inserts a node into an AVL tree. Compare it to an implementation in C in Listing 2. The Rust code looks like gibberish.

use std::mem::{replace, swap};
impl<'a, T: 'a + Ord> AvlNode<T> {
    fn rotate_right(&mut self) -> bool {
        if self.left.is_none() {
            return false;

        // 1. Take nodes A and C
        let left_node = self.left.as_mut().unwrap();
        let left_right_tree = left_node.right.take();
        let left_left_tree = left_node.left.take();

        // 2. Link A node to left node
        let mut new_right_tree = replace(&mut self.left, left_left_tree);
        // 3. Swap B and D node value to avoid moving the root
        swap(&mut self.value, &mut new_right_tree.as_mut().unwrap().value);
        // 4. Take E node
        let right_tree = self.right.take();

        // 5. Link C and E nodes to swapped D node
        let new_right_node = new_right_tree.as_mut().unwrap();
        new_right_node.left = left_right_tree;
        new_right_node.right = right_tree;
        // 6. Link swapped D node to root right node
        self.right = new_right_tree;

        if let Some(node) = self.right.as_mut() {


Listing 1: Rust code to insert a note into an AVL tree.

node * rotate_right(node *x)
    node *y;

Listing 2: C version of Listing 1.

Then there is C++ that was intended to be a better, more user-friendly C. In practice, however, C++ has become a flawed, full of compromises, bulky behemoth that inherited many problems of C language. Short of large-scale systems (such as trading systems) it is hard to find a place for C++. Perhaps utilizing a subset of C++ would make it worth using. Yet, beyond any doubt, embedded systems projects are almost never going to be good candidates for C++.

Embedded systems

Back to embedded systems. By a wider definition these systems are integrated systems intended to carry out a single task, and do it optimally. The task can be extremely simple like switching a light on the timer, or complex like a module of a pilot assistance system or a power grid controller. The underlying hardware resources are always utilized to the full extent: the light switch must be cheap, so the less CPU and memory is used the better; the aircraft on-board computer must limit its power use, and therefore uses a just-powerful-enough CPU.

These embedded systems are all around us. Today, every car has multiple embedded systems. So do stoves, air conditioners, cell phones, fridges, headphones, network routers, watches, cash registers, ATMs, and traffic systems. The list goes on and on.  Every industrial plant has hundreds if not thousands of systems. Programming those close-to-hardware systems in the “alternative” languages is often impossible; there is no means to access hardware interrupts, peripherals and other hardware resources in any high-level language but C. Moreover, operating systems’ kernels that form the foundations of embedded devices and provide programming services (APIs) are themselves are written in C.

Furthermore, modern embedded systems include two categories that are often overlooked.

The first category are systems that require various safety-critical certifications: MISRA certifications, FAA, FRA, ISO 26262 (automotive safety), etc. The certification processes are rigorous and in essence require inspection of and justification for every single line of a system’s code. This is not a task that can be accomplished if the systems’ language is Go or Python — I am not aware of certification procedures for any language other than C. To complicate things, the development tools — compilers, etc., are also certified. Low-level development tools are always written in C. Thus, unless we are ready to bypass certifications and bend on safety, the developers of those systems are bound to use C for the foreseeable future.

The second often overlooked category is real-time systems — those that are deterministic in nature. Real-time systems can again be simple and harmless such as an inkjet printer that must deliver dye on time, or those that carry a lot of weight such as a radiotherapy equipment that delivers a measured dose of radiation to a specific location. These systems can never be programmed in any other language but C.


C is not ideal. That said, the question whether it is time to retire the language or even attempt to do so is a moot point. Replacing C is, in my view, unrealistic as it is often the only alternative to any other programming language.

Andrei Gorine, McObject co-founder, leads the company’s product engineering as CTO. He’s held senior positions with leading embedded systems and database software companies, and his experience in providing embedded storage solutions in such fields as industrial control, industrial preventative maintenance, satellite and cable television, and telecommunications equipment is highly recognized in the industry. Mr. Gorine has published articles and spoken at many conferences on topics including real-time database systems, high availability, and memory management. Over the course of his career he has participated in both academic and industry research projects in the area of real-time database systems. Mr. Gorine holds a Master’s degree in Computer Science from the Moscow Institute of Electronics and Mathematics, and is a member of IEEE and ACM.

Related Contents:

For more Embedded, subscribe to Embedded’s weekly email newsletter.

3 thoughts on “C is alive and well

  1. First of all I agree that C will still take some time to get out of the market.

    But the argument for invalidating Rust is weak and almost unfair, it’s not implemented *exactly* the same thing, apart from the comments to give the impression of being too much. There are other arguments for Rust not being a viable option at this point, such as support still being very premature and being full of broken stuff. But no arguments for not being a valid option when you have decent support for the vast majority of devices.

    Log in to Reply
  2. Well said. As I’ve watched battle after battle to replace C for years, I think back to the day when I was programming in assembly language. C allows me to do almost everything I could do in assembly language, but in a high level, transportable language. No other language that I am familiar with allows such access, control, and flexibility. C++ may come close, but at a high price on limited resource embedded systems.

    Log in to Reply
  3. The presented argument for C and against Rust seems a bit weak because it’s always possible to find a snippet of code that looks better in one language than the other.

    A much stronger argument is that attempts to build a safer language for embedded systems have been tried before with limited success. For example, is Rust that much better and safer than Ada/SPARK?

    Another point is that safer subsets of C, such as MISRA-C or CERT-C exist, and go a long way towards improving safety and reliability of source code. Consequently, anybody genuinely interested in improving code safety and reliability should routinely use a static analysis tool to automatically enforce a safer subset of C. This is much cheaper than jumping into an entirely new language like Rust.

    Yet another way to improve quality of source code is to raise the level of abstraction and use automatic code generation, for example from hierarchical state machines. It turns out that the target programming language (C, C++, Ada, or Rust) is of secondary importance, just like object code emitted by a traditional compiler.

    Log in to Reply

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.