How to Set Up Static Code Analysis in an Embedded C Project

If you've ever shipped a firmware update only to discover a null pointer dereference hiding in production โ you'll know the pain I'm talking about.
Static code analysis is one of the most underused tools in embedded development. It costs nothing to set up, catches bugs before they reach hardware, and makes your codebase dramatically safer. Yet most embedded teams I've worked with either skip it entirely or bolt it on as an afterthought.
In this post I'll walk you through exactly how to set up static analysis in an Embedded C project โ from scratch, in under an hour.
What Is Static Code Analysis?
Static analysis tools examine your source code without executing it. They look for:
Null pointer dereferences
Memory leaks
Uninitialised variables
Buffer overflows
Unreachable code
Misuse of standard library functions
Think of it as a highly pedantic code reviewer that never gets tired, never misses a line, and works for free.
Why It Matters in Embedded C
In embedded systems, bugs don't just crash an app โ they can brick a device, create a security vulnerability, or cause a safety-critical failure in the field. Firmware is often running on hardware with no MMU, no OS protection, and no easy way to recover from a hard fault.
Static analysis gives you a safety net before the code ever touches hardware.
During my time working on security-critical STB platforms, static analysis caught entire classes of bugs that code review and unit tests missed โ especially around pointer arithmetic and integer overflow in protocol parsing code.
Tools You Need
For Embedded C projects, these are the two tools I recommend starting with:
1. Cppcheck
Free, open source, and excellent for C. Easy to integrate into any build system.
# Install on Ubuntu/Debian
sudo apt-get install cppcheck
# Install on macOS
brew install cppcheck
2. Clang-tidy
Part of the LLVM toolchain. More powerful, but requires a compilation database to work well with embedded projects.
# Install on Ubuntu/Debian
sudo apt-get install clang-tidy
We'll start with Cppcheck today โ it's the quickest win with zero configuration overhead.
Step 1: Run Cppcheck on Your Project
Navigate to your project root and run:
cppcheck --enable=all --inconclusive --std=c99 src/
What these flags do:
| Flag | Purpose |
|---|---|
--enable=all |
Enables all checks (style, performance, portability, warnings) |
--inconclusive |
Reports issues even when not 100% certain โ catches more bugs |
--std=c99 |
Tells Cppcheck which C standard you're targeting |
src/ |
Path to your source directory |
You'll see output like this:
[src/parser.c:42]: (error) Null pointer dereference: ptr
[src/uart.c:78]: (warning) Uninitialised variable: rx_buffer
[src/config.c:15]: (style) Variable 'timeout' is assigned a value that is never used
Each line tells you: file, line number, severity, and description. Start with error severity first โ those are the critical ones.
Step 2: Suppress False Positives
Cppcheck sometimes flags code that is intentionally written a certain way โ especially in embedded C where you do things like cast integers to pointers for memory-mapped registers.
Suppress these cleanly using inline comments:
/* cppcheck-suppress invalidPointerCast */
volatile uint32_t *reg = (uint32_t *)0x40021000;
Or use a suppressions file for project-wide suppressions:
# suppressions.txt
invalidPointerCast
missingIncludeSystem
cppcheck --suppressions-list=suppressions.txt --enable=all src/
This keeps your output clean and focused on real issues.
Step 3: Integrate Into Your Makefile
Running Cppcheck manually is useful once. Running it automatically on every build is what actually changes your codebase quality.
Add this to your Makefile:
CPPCHECK = cppcheck
CPPCHECK_FLAGS = --enable=all --inconclusive --std=c99 --error-exitcode=1
.PHONY: static-analysis
static-analysis:
\((CPPCHECK) \)(CPPCHECK_FLAGS) src/
# Add to your build target so it runs automatically
all: static-analysis build
The --error-exitcode=1 flag is important โ it makes Cppcheck return a non-zero exit code if errors are found, which means your build fails if static analysis fails. This enforces quality at the build stage.
Step 4: Add to Your CI Pipeline
If your team uses any CI/CD (Jenkins, GitHub Actions, GitLab CI), add the static analysis step there too. Here's a simple GitHub Actions example:
name: Static Analysis
on: [push, pull_request]
jobs:
cppcheck:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install Cppcheck
run: sudo apt-get install -y cppcheck
- name: Run Cppcheck
run: cppcheck --enable=all --std=c99 --error-exitcode=1 src/
Now every pull request gets automatically checked before it merges. No more "it looked fine in review" surprises.
Common Issues You'll Find in Embedded C
Here are the most frequent findings I've seen across embedded C codebases:
1. Uninitialised variables
uint8_t status; // โ uninitialised
if (status == 0x01) { } // undefined behaviour
uint8_t status = 0x00; // โ
always initialise
2. Integer overflow in calculations
uint8_t a = 200;
uint8_t b = 100;
uint16_t result = a + b; // โ overflow happens before assignment
uint16_t result = (uint16_t)a + (uint16_t)b; // โ
cast first
3. Unchecked return values
read_sensor(); // โ ignoring return value
int ret = read_sensor(); // โ
always check
if (ret != STATUS_OK) {
handle_error(ret);
}
What to Do Next
Once Cppcheck is integrated and your obvious errors are fixed, the next step is to bring in clang-tidy for deeper analysis โ it can catch more subtle issues around type safety, API misuse, and modernisation opportunities.
I'll cover that in a future post.
For now, start with this:
Install Cppcheck
Run it on your project today
Fix all
errorseverity findings firstAdd it to your Makefile
Commit the suppressions file to version control
Your future self โ and your team โ will thank you.
Resources
If you want to go deeper on writing safer embedded C, these books are worth keeping on your shelf:
Making Embedded Systems โ Elecia White (great for practical embedded patterns)
Embedded C Coding Standard โ Barr Group (free PDF, industry standard)
Found this useful? Follow the blog for more practical embedded systems guides. Next up: Yocto for beginners โ what it is and why embedded teams use it.
โก Affiliate Disclosure: Some links on this blog are affiliate links. If you buy through them, I may earn a small commission at no extra cost to you. I only recommend what I genuinely find useful.


