Introduction: Why Care About Safer C?

Hello engineers and fellow developers at www.123microcontroller.com! Today, we are going to discuss the “heart and soul” of building reliable, industrial-grade hardware products: the concept of “Writing Safer C”.

We all know C is a powerful language that has stood the test of time for nearly 50 years. However, it’s also notorious for making it difficult to write highly reliable software. In the world of embedded systems, a software crash doesn’t just result in a blue screen and a reboot. It could mean a burnt-out motor, a failed smart braking system, or even endangering human lives! Today, we’ll dive into how industry experts recommend elevating our C code to a “Safer C” standard for embedded applications.

Core Concept: The Philosophy of Safer C

Writing C code that “works” is relatively easy, but writing C code that is safe and reliable is an entirely different ball game. Here are the root causes of the problem and how we can address them:

  • The Gray Areas of the C Standard: The ISO C standard leaves many gray areas, such as Implementation-defined, Unspecified, and Undefined Behaviors (UB). This means the exact same code compiled on different chips (e.g., migrating from ESP32 to STM32) can behave completely differently.
  • The Goal of Writing Safer C: The essence of writing safer C is learning “how NOT to write C incorrectly.” Good code shouldn’t just do what we intend it to do; more importantly, “it must never do anything we didn’t intend for it to do.”
  • Prevention over Debugging: Writing Safer C means utilizing techniques and tools to build reliability into the code from the very beginning, rather than scrambling to debug a faulty system later.
  • Coding Standards as a Shield: Since no programmer writes perfect code, the industry has established “traffic rules” for C programming:
    • MISRA C: A mandatory standard for safety-critical systems, where failure could result in injury or death. MISRA restricts dangerous C features, utilizing only a safe “subset” of the language.
    • BARR-C: A standard focused on reducing bugs and improving reliability in general embedded applications, designed to be highly compatible with MISRA C.
    • SEI CERT C: A security-focused standard aimed at patching vulnerabilities to prevent hacking, largely by eliminating Undefined Behaviors. In safety-critical applications, rules can be even stricter than CERT C—like forcing strict static memory allocation (forbidding malloc).

Safer C Best Practices Diagram

Code Example: Upgrading Regular C to Safer C

Let’s look at an example of how average C code can be hardened using Safer C principles (focusing on array bounds checking and static memory allocation).

#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>

/* 1. Avoid Magic Numbers for clarity and maintainability */
#define MAX_SENSOR_LOGS (100U)

/* 
 * 2. Avoid Dynamic Memory Allocation (malloc/free).
 * In Safety-Critical systems, always prefer Static Allocation 
 * to prevent Memory Leaks and sudden Out-Of-Memory errors.
 */
typedef struct {
    uint16_t data[MAX_SENSOR_LOGS];
    size_t   count;
} SensorBuffer;

/* Pre-allocate memory globally/statically */
static SensorBuffer temp_sensor = { .count = 0 };

/* 
 * 3. Always perform Bounds Checking on incoming data 
 * to prevent Buffer Overflows (a severe Undefined Behavior).
 */
bool log_sensor_data(SensorBuffer *buffer, uint16_t new_val) {
    /* Prevent Null Pointer Dereference */
    if (buffer == NULL) {
        return false;
    }

    /* Ensure the index does not exceed the array bounds */
    if (buffer->count < MAX_SENSOR_LOGS) {
        buffer->data[buffer->count] = new_val;
        buffer->count++;
        return true;
    }
    
    /* Alert when the buffer is full, rather than corrupting adjacent memory */
    return false; 
}

int main(void) {
    if (!log_sensor_data(&temp_sensor, 1024)) {
        printf("Error: Cannot log data.\n");
    }
    return 0;
}

Best Practices to Watch Out For

To upgrade your skills to a hardcore engineer level, experts suggest these Safer C best practices:

  1. Eliminate All Undefined Behaviors: This is the root cause of many global software vulnerabilities. Beware of uninitialized pointers, out-of-bounds array access, and division by zero.
  2. Be Wary of the Standard Library: Several basic C stdlib functions have unsafe behaviors. Whenever possible, use safer alternatives (like the _s bounded functions in C Standard Annex K) or write your own wrapper layers.
  3. Always Check Return Status: Never ignore the return values (Status Information) from system functions. Always verify that a function executed successfully before proceeding with its data.
  4. Use Static Analysis Tools: Knowing standards like CERT C or MISRA C is great, but manually reviewing code is tough. Rely on tools like SCALe (Source Code Analysis Laboratory) or commercial static analyzers to scan for standard compliance before flashing to your board.

Conclusion

Writing Safer C isn’t just about memorizing syntax; it’s an attitude and discipline centered on reliability and safety. Adopting standards like MISRA C, BARR-C, or CERT C is like having a master architect review your house blueprints—ensuring your embedded systems run smoothly, accurately, and safely in the real world.

No single book will make your C code 100% perfect. Everything requires practice! If you’re interested in diving deeper into debugging techniques or discovering great static analysis tools, keep reading and sharing knowledge with us at www.123microcontroller.com. See you in the next article. Happy Coding!