ARIN UPADHYAY | Embedded Systems & Low-Level C Developer

HOME | PROJECTS | BLOG | OTHER

29-08-2025

How I Code in C

C is one of my favorite languages, but it comes with a reputation: powerful, fast but dangerous if you're careless. Over time, I've developed my own set of rules and habits that keep my C code safe, clean, and consistent. This post is basically how I like to structure projects, name things, and enforce safety.

Naming Convention:
I keep naming dead simple and consistent across every project:

  • snake_case: variables and functions
  • PascalCase: types
  • SCREAMING_SNAKE_CASE: preprocessors

Project Structure:
I hate messy include chains and header spaghetti, so I stick to a simple layout:

  • common.h: my “universal” header. It has #pragma once, all config macros, typedefs, and stdint.h
  • <module>.c: include common.h or module related .h/.c files
  • main.c: entry point with the general algorithm. include common.h or related .h/.c files.

Safety Rules/Guide:
To avoid the classic “C pitfalls” and for easy remembering, I have simplified (or maybe oversimplified) the 10 rules from The Power of 10: Rules for Developing Safety-Critical Code by Gerard J. Holzmann. In my opinion, this list should treated more like a optional checklist or a guide than a rulebook because not every application needs to be absolutely safety-critical but it never hurts to be more than less.

  1. No goto, recursion, or setjmp/longjmp (control and scope safety)
  2. Variables declared as local as possible (control and scope safety)
  3. Only one level of deref, no function pointers, no hidden deref (memory safety)
  4. No dynamic memory after initialization (memory safety)
  5. Check parameters and non-void return values (bounds and check safety)
  6. At least two asserts per function (bounds and check safety)
  7. Every loop must either have a clear upper bound or be provably non-terminating (bounds and check safety)
  8. Functions stay under ~60 lines (meta)
  9. Preprocessor for only headers and simple macros (meta)
  10. Clean pass compilation with all warnings, pedantic, and static analysis (meta)

Recommended:

  • Arena allocators: a perfect fit for rule #4
  • Dynamic arrays: flexible storage without manual reallocation boilerplate
  • stb-style/header-only libraries: drop-in, minimal dependencies, easy to use

By keeping naming, structure, and safety consistent, I can open up a project months later and instantly feel at home in the code.