5 forbidden C features that aren't (necessarily)
Best practices and industry standards represent a snap shot of guiding wisdom, but they do develop and evolve over time. Unfortunately, they can be slow to evolve and often become entrenched despite technological advances that void the previously known best practice. The use of C language features within the embedded systems space has suffered the same fate. Many best practices have their roots in the 80’s and 90’s when compilers were quirky and microcontrollers were truly resource constrained. But compilers and microcontrollers have come a long way since then, and with the improvements many “forbidden” features and design techniques just might not be so anymore. Here are five traditionally forbidden features that deserve re-evaluation.
Feature #1 – float
The use of float in embedded systems has for a very long time been strongly opposed. Float traditionally has had a number of potential problem sources, the first of which was that microcontrollers didn’t have floating point units (FPUs). For the most part this statement remains true today for the lowest end microcontrollers. But the cost to include an FPU has come down drastically to the point that mid-range and general microcontrollers are starting to include an FPU. Advances in microcontroller technology also now include hardware acceleration for mathematical functions, which even without an FPU can aid in faster calculations.
Second, the use of float without an FPU requires the compiler to pull in software libraries that are bulky and slow. But compiler technology has drastically improved since the late 20th century and performing a floating-point calculation, even on an 8-bit microcontroller in software, is optimized to the point having negligible impact. Don’t believe me? Try it! Results will vary and may even align with conventional wisdom but the fact is that technology is changing and developers need to change with it.
Feature #2 – malloc
malloc allows a developer to dynamically allocate memory during program execution and is potentially a very dangerous tool when improperly used within an embedded system. Traditionally the use of malloc has been completely prohibited in resource-constrained systems and for good reasons. What happens when the heap becomes fragmented? How does a developer handle a failure to allocate memory? What about memory leaks? These are difficult problems that require code space and horsepower to handle, which the traditional microcontroller can’t.
But microcontrollers are no longer “traditional." A low-cost microcontroller in 2015 may have clock speeds running in excess of 200 MHz, more than 1 MB of flash space, and RAM upwards of 64 KB (as high as 256 MB). Even in a system that isn’t quite this beefy, the tools for properly implementing and using malloc are available. The application may very well warrant it as well, so it shouldn’t from the start be thrown out of considerations just because of outdated best practices.
Feature #3 – printf
In general, the use of C library functions within an embedded system is considered to be poor practice. Most of the C library is not reentrant, is usually bulky and slow to execute (or is it?), or is implemented in such a way that it blocks execution until complete. The use of printf falls into many of these categories and has been shunned from use in embedded systems. Embedded developers often feel guilty for using printf or in conversations will say, “I know I shouldn’t have but I added printf …”
As previously mentioned a lot has changed and between compiler optimizations and hardware advancements the use of printf is the guilty pleasure it used to be. The function, while considered “large,” takes up little code space given the typical 32 kB flash space. Typical implementations have printf act as a blocking function, which can affect real-time response and hog a potential shared resource. Yet response issues can easily be overcome by linking printf to a circular buffer and interrupt driver transmit driver that allows program execution to continue while the driver does the necessary work. The circular buffer helps keep messages ordered as first come first serve.
Feature #4 – memset
Most C features that involve memory manipulation or dynamic memory end up on the forbidden feature list. The reason is pretty obvious: many developers and teams have gotten burned in the past using such features. Rather than get burned again they moved such features into the "never use" category.
When a feature such as malloc or memset make the most sense, though, they shouldn’t be shied away from. Instead, developers should make sure that they completely understand how to use the feature, what precautions are needed for proper use, how to recover in the event the worst case happens, and then, of course, proper testing to ensure that everything goes according to plan.
Feature #5 – C bit fields
One concept whose use is not rooted in the advancement of compiler technology or hardware advances is the use of features that are ambiguously defined in the C standard. A great example of such a feature is a bit field. The biggest problem with using bit fields is that there are usually portability issues. For instance, the fact bit ordering isn’t standard means that the compiler often can rearrange the bits to what it decides is the most efficient means of implementation. There are also issues with padded bytes being added to the overall structure.
The general rule of thumb, then, has been to avoid the use of bit fields. But there are plenty of instances where they make sense and even where the portability issues don’t matter. A great example of where using bit fields makes sense is for creating a structure that is used to create a configuration table for initializing a driver or an application. With the initialization written to read the value of the bit, reordering or even padded bytes won’t be an issue.
Conclusions Standards and best practices are designed to help prevent developers from shooting themselves in the foot, but they are just that -- best practices. A standard may say that the use of a C feature is forbidden but the fact of the matter is that it is up to the developer in his or her own unique situation to determine whether conventional wisdom applies. Challenging our preconceptions and ensuring we understand why those best practices are in place is just as important as following the practices. Don’t write-off features without understanding their application, the risks, and the rewards. Developers need to understand their timing, performance, and size constraints when considering the use of these “forbidden” features.
What other features have you encountered that work in today's development environment but traditionally been considered forbidden?
Jacob Beningo is a Certified Software Development Professional (CSDP) whose expertise is in embedded software. He works with companies to decrease costs and time to market while maintaining a quality and robust product. Feel free to contact him at firstname.lastname@example.org, at his website www.beningo.com, and sign-up for his monthly Embedded Bytes Newsletter here.
Join over 2,000 technical professionals and embedded systems hardware, software, and firmware developers at ESC Silicon Valley July 20-22, 2015 and learn about the latest techniques and tips for reducing time, cost, and complexity in the embedded development process.
Passes for the ESC Silicon Valley 2015 Technical Conference are available at the conference’s official site with discounted advance pricing until July 17, 2015. The Embedded Systems Conference and EDN are owned by UBM Canon.