C is alive and well

The debate about phasing out the C language has been going on for years, 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 founded, but I believe the conclusions are misleading. The debate about getting rid of the C language has been going on 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 forever to develop a product for sale. Opponents often argue that it depends on the task at hand – the pervasive “find the best tool for the task” attitude and supporting legacy systems (of which there are many) are almost impossible to maintain without C. My perspective is slightly different Perspective: In today’s software industry, is it even feasible to retire C? Also, my perspective is largely the design and development of embedded systems.

Half a century has passed since the introduction of C. Before C, the art of programming was actually “art”. Since the introduction of C, the art has quickly evolved into a proper profession. Complex projects have been planned and put together to improve all aspects of life: software now controls everything from a simple electrical plug to complex spacecraft systems to social networking systems. There’s no question that all of these life-changing advances wouldn’t be possible without a simple tool like C.

There are many alternatives that are being used successfully and legitimately to support software ecosystems. So how important is C in a modern software development toolset? Perhaps it could be replaced by modern alternatives? Let’s consider these alternatives.

Alternatives to C

Alternatives could be “safe” languages ​​like Java, C# and Go. One of the main problems with these languages ​​is that, by design, they suck up system resources like a vacuum cleaner. Security is primarily achieved through the use of a garbage collection (GC) process that securely handles memory allocations so that allocated objects never use the contents of other objects. In the beginning, GCs had to iterate over all object references to ensure that the object in question was out of reach. This would “overheat” the memory bus, especially when the number of objects allocated by the application is significant. Many improvements have been made to garbage collectors over the years – background GC, multi-threaded garbage collection, generational collectors, etc. etc. As a result, modern garbage collectors in languages ​​like Java are not extremely expensive from a performance standpoint. However, in embedded systems where memory is scarce, garbage collectors are often required to perform the cleanup, and often the overhead involved is unacceptable.

Another “safe” alternative is Rust. No question, Rust’s idea is simply wonderful: “compiled means executed”. In practice, however, using Rust is so complicated that it all too often feels like “coding for the sake of programming”. Development in Rust becomes a struggle with the language instead of solving the pressing problems.

For example, consider Rust’s approach to working with pointers, a common programming practice in C. It’s so complicated it’s almost useless. Listing 1 shows 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() {
            node.update_height();
        }

        self.update_height();
        true
    }
}

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

node * rotate_right(node *x)
{
    node *y;
    y=x->left;
    x->left=y->right;
    y->right=x;
    x->ht=height(x);
    y->ht=height(y);
    return(y);
}

Listing 2: C version of Listing 1.

Then there’s C++, which should be better, more user-friendly C. In practice, however, C++ has become a buggy, uncompromising, bulky behemoth that inherited many of the problems of the C language. Except for large systems (like trading systems) it’s hard to find a place for C++. Maybe it would be worth using a subset of C++. Still, embedded systems projects are almost never good candidates for C++.

Embedded Systems

Back to embedded systems. In a broader sense, these systems are integrated systems designed to perform a single task, 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 fully utilized: the light switch must be cheap, so the less CPU and memory used, the better; The aircraft’s onboard computer needs to limit its power consumption, so it uses a CPU that’s just powerful enough.

These embedded systems are all around us. Today, every car has multiple embedded systems. So are stoves, air conditioners, cell phones, refrigerators, headphones, network routers, clocks, cash registers, ATMs, and transportation systems. The list goes on and on. Every industrial facility has hundreds, if not thousands, of systems. Programming these hardware-related systems in the “alternative” languages ​​is often impossible; There is no way to access hardware interrupts, peripherals, and other hardware resources in any high-level language other than C. In addition, the operating system kernels that form the basis of embedded devices and provide programming services (APIs) are themselves written in C.

In addition, modern embedded systems fall into 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 essentially require the testing and justification of every single line of a system code. This is not a task that can be completed if the system language is Go or Python – I am not aware of any certification procedures for languages ​​other than C. To complicate things even more, the development tools – compilers, etc. – are also certified. Low-level development tools are always written in C. So unless we’re willing to bypass certifications and pay attention to security, the developers of these systems will be using C for the foreseeable future.

The second category that is often overlooked is real-time systems—those that are inherently deterministic. Real-time systems, on the other hand, can be simple and harmless, like an inkjet printer that needs to deliver color on time, or ones that carry a lot of weight, like a radiation therapy machine that delivers a measured dose of radiation to a specific location. These systems can never be programmed in any language other than C.

Conclusion

C is not ideal. However, whether it is time to withdraw the language, or even to try, is a moot point. Replacing C is unrealistic in my opinion, since it is often the only alternative to other programming languages.


Andrei Gorine, McObject Co-founder, leads the company’s product development as CTO. He has held senior positions at leading embedded systems and database software companies, and his experience in providing embedded storage solutions in areas such as industrial control, industrial predictive maintenance, satellite and cable television, and telecommunications equipment is well recognized in the industry. Mr. Gorine has published articles and spoken at many conferences on topics such as real-time database systems, high availability, and storage management. During his career he has participated in both academic and industrial research projects in the field 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 content:

For more embedded, Subscribe to Embedded’s weekly email newsletter.



Comments are closed.