C Programming: Low-Level Mastery
HomeInsightsCoursesC ProgrammingCoding Standards (MISRA C, CERT C)
Best Practices

Coding Standards

Master professional C coding standards: naming conventions, formatting, comments, code organization, and style guides. Learn industry best practices for writing clean, maintainable, and consistent C code.

Why Coding Standards Matter

Coding standards ensure consistency across teams and projects. Consistent code is easier to read, maintain, and debug. Standards reduce cognitive load, prevent errors, and improve collaboration. Whether following established guides (Linux, Google, MISRA) or creating your own, consistency is key.

C
/* Benefits of coding standards */
/*
   - Readability: Easier to understand
   - Maintainability: Simpler to modify
   - Bug prevention: Consistent patterns reduce errors
   - Onboarding: New developers productive faster
   - Code reviews: Focus on logic, not style
   - Tool compatibility: Linters, formatters work better
*/

/* Common style guides */
/*
   - Linux Kernel Coding Style
   - Google C++ Style Guide (C parts)
   - GNU Coding Standards
   - MISRA C (safety-critical)
   - NASA C Style Guide
   - CERT C Coding Standard (security)
   - Embedded C Coding Standard
*/

/* Key areas of coding standards */
/*
   1. Naming conventions
   2. Formatting and indentation
   3. Comments and documentation
   4. File organization
   5. Function guidelines
   6. Error handling
   7. Memory management
   8. Type usage
*/

Naming Conventions

Good names communicate purpose. Use descriptive names, consistent patterns, and appropriate cases. Avoid abbreviations unless standard. Name length should match scope: short for local, descriptive for global. Follow project conventions.

C
/* Variable naming */

/* BAD: Unclear abbreviations */
int n, cnt, tmp, x, y;

/* GOOD: Descriptive names */
int node_count, customer_count, temporary_buffer;

/* Constants: ALL_CAPS with underscores */
#define MAX_BUFFER_SIZE 1024
#define DEFAULT_TIMEOUT 30
#define PI 3.14159

/* Or enums */
enum {
    MAX_CONNECTIONS = 100,
    MIN_POOL_SIZE = 10
};

/* Functions: verb_noun or action style */
void initialize_system(void);
void process_request(Request *req);
int calculate_checksum(const uint8_t *data, size_t length);

/* Or verb only for short operations */
void init(void);
void cleanup(void);
void run(void);

/* Types: PascalCase or snake_case with _t */
typedef struct Customer Customer;  /* PascalCase */
typedef struct http_request http_request_t;  /* snake_case_t */

/* Struct members: snake_case */
struct Point {
    int x_coordinate;
    int y_coordinate;
};

/* Or just short names in obvious context */
struct Point {
    int x;
    int y;
};

/* Macros: ALL_CAPS */
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

/* Global variables: g_ prefix (or avoid globals) */
int g_connection_count = 0;
Config *g_application_config = NULL;

/* Static (file-scope): s_ prefix or no prefix */
static int s_instance_counter = 0;
static Buffer s_shared_buffer;

/* Pointers: p prefix (optional, style dependent) */
Node *pNode;  /* Some prefer */
Node *node;   /* Others prefer */

/* Boolean variables: is_, has_, can_, should_ */
bool is_initialized = false;
bool has_permission = true;
bool can_write = false;
bool should_retry = true;

/* Length/Count: _count, _length, _size */
size_t buffer_length;
int connection_count;
size_t array_size;

/* Index: _index, _idx, or just i, j, k */
int current_index;
for (int i = 0; i < count; i++) { /* ... */ }

/* Callbacks: _callback, _handler, on_ prefix */
void on_connection_established(void);
typedef void (*ErrorHandler)(int code);
void register_click_handler(void (*callback)(void));

/* Platform-specific: Prefix or suffix */
#ifdef _WIN32
    void win32_initialize(void);
#endif

#ifdef __linux__
    void linux_initialize(void);
#endif

/* Private/Internal: _ prefix or static */
static void _internal_helper(void);  /* Convention: _ means private */

/* Hungarian notation (controversial, falling out of favor) */
int nCount;       /* n = integer */
char *szName;     /* sz = null-terminated string */
bool bFlag;       /* b = boolean */
/* Most modern guides recommend against this */

/* Module prefix */
/* For library code, prefix with module name */
json_parse(...);
json_stringify(...);
json_free(...);

http_get(...);
http_post(...);

/* Naming conventions summary */
/*
   Variables: snake_case or camelCase
   Functions: snake_case
   Types: PascalCase or snake_case_t
   Constants: ALL_CAPS
   Macros: ALL_CAPS
   Globals: g_ prefix (or avoid)
   Static: s_ prefix or no prefix
   Private: _ prefix or static
*/

/* Avoid */
/* Single-letter names (except i, j, k in loops) */
/* Ambiguous abbreviations */
/* Inconsistent patterns */
/* Names that differ only by case */
/* Reserved keywords with modifications */

Formatting and Indentation

Consistent formatting improves readability. Choose a style (K&R, Allman, GNU) and stick to it. Use spaces or tabs consistently. Line length 80-120 characters. Automated formatters (clang-format, indent) enforce consistency.

C
/* Brace styles */

/* K&R style (Kernighan & Ritchie) - Most common */
int function(int x) {
    if (x &gt; 0) {
        return x;
    } else {
        return -x;
    }
}

/* Allman style (BSD) */
int function(int x)
{
    if (x &gt; 0)
    {
        return x;
    }
    else
    {
        return -x;
    }
}

/* GNU style */
int
function (int x)
{
  if (x &gt; 0)
    {
      return x;
    }
  else
    {
      return -x;
    }
}

/* Linux kernel style (variant of K&R) */
int function(int x)
{
	if (x &gt; 0) {
		return x;
	} else {
		return -x;
	}
}

/* Indentation */

/* Use tabs OR spaces, never mix */
/* 4 spaces is common, tabs = 8 (Linux kernel) */

/* GOOD: Consistent indentation */
void process(void)
{
    if (condition) {
        do_something();
        if (nested) {
            do_more();
        }
    }
}

/* BAD: Inconsistent indentation */
void process(void)
{
  if (condition) {
      do_something();
    if (nested) {
         do_more();
    }
  }
}

/* Line length */

/* Keep lines under 80-120 characters */

/* GOOD: Break long lines */
int result = very_long_function_name(
    first_parameter,
    second_parameter,
    third_parameter
);

/* Or align parameters */
int result = very_long_function_name(first_parameter,
                                     second_parameter,
                                     third_parameter);

/* BAD: Too long */
int result = very_long_function_name(first_parameter, second_parameter, third_parameter, fourth_parameter, fifth_parameter);

/* Whitespace */

/* GOOD: Whitespace around operators */
int x = a + b * c;
if (x &gt; 10 && y < 20) {"{ }"}

/* BAD: No whitespace */
int x=a+b*c;
if(x>10&&y<20){"{ }"}

/* GOOD: Space after keywords */
if (condition)
for (int i = 0; i < 10; i++)
while (running)

/* BAD: No space */
if(condition)
for(int i=0;i<10;i++)
while(running)

/* GOOD: Space after comma */
function(a, b, c);

/* BAD: No space */
function(a,b,c);

/* Blank lines */

/* Use blank lines to separate logical blocks */
void function(void)
{
    /* Initialization */
    int x = 0;
    int y = 0;

    /* Processing */
    for (int i = 0; i < 10; i++) {
        x += i;
        y += i * 2;
    }

    /* Cleanup */
    print_results(x, y);
}

/* Alignment */

/* Align related items (optional, style-dependent) */
#define MAX_NAME     100
#define MAX_ADDRESS  200
#define MAX_PHONE    20

int    count   = 0;
float  average = 0.0;
double sum     = 0.0;

/* Or don't align (simpler, less diff noise) */
#define MAX_NAME 100
#define MAX_ADDRESS 200
#define MAX_PHONE 20

/* Pointer declaration */

/* Style 1: Attach to type */
int* ptr;
char* string;

/* Style 2: Attach to name (more common) */
int *ptr;
char *string;

/* Problem with style 1: */
int* a, b;  /* a is pointer, b is int (confusing!) */

/* Style 2 is clearer: */
int *a, b;  /* Clearly: a is pointer, b is int */

/* Automated formatting */

/* clang-format configuration */
/* .clang-format */
/*
BasedOnStyle: LLVM
IndentWidth: 4
ColumnLimit: 100
PointerAlignment: Right
BreakBeforeBraces: Attach
*/

/* Format code: */
/* clang-format -i file.c */

/* GNU indent */
/* indent -kr -i4 file.c */

Comments and Documentation

Good comments explain why, not what. Code shows what; comments explain intent, rationale, and non-obvious behavior. Use Doxygen for API documentation. Keep comments up-to-date. Avoid redundant comments. Document public interfaces thoroughly.

C
/* Good vs Bad comments */

/* BAD: Obvious comment */
i++;  /* Increment i */

/* GOOD: Explain why */
i++;  /* Skip null terminator */

/* BAD: Redundant */
/* Set x to 10 */
x = 10;

/* GOOD: Explain reasoning */
/* Default timeout is 10 seconds per RFC 1234 */
timeout = 10;

/* GOOD: Explain complex algorithm */
/*
 * Use binary search to find insertion point.
 * This is O(log n) compared to O(n) for linear search.
 * Trade-off: More complex code for better performance.
 */
int index = binary_search(array, size, value);

/* GOOD: Explain non-obvious behavior */
/* 
 * Returns -1 on error, NOT errno.
 * Caller must check errno separately.
 */
int result = complex_operation();

/* File header comments */
/**
 * @file network.c
 * @brief Network communication module
 * @author John Doe
 * @date 2024-01-01
 * @copyright MIT License
 * 
 * This module handles all network I/O operations including
 * connection management, data transmission, and error handling.
 */

/* Function documentation (Doxygen style) */

/**
 * @brief Calculates checksum of data buffer
 * 
 * Computes CRC32 checksum for data integrity verification.
 * 
 * @param data Pointer to data buffer (must not be NULL)
 * @param length Length of data in bytes
 * @return CRC32 checksum value
 * @retval 0 if data is NULL or length is 0
 * 
 * @note This is computationally expensive for large buffers
 * @warning Data must remain valid during calculation
 * @see validate_checksum()
 * 
 * @par Example:
 * @code
 * uint32_t crc = calculate_checksum(buffer, size);
 * if (crc != expected) {
 *     // Handle error
 * }
 * @endcode
 */
uint32_t calculate_checksum(const uint8_t *data, size_t length);

/* Structure documentation */

/**
 * @struct Connection
 * @brief Represents a network connection
 * 
 * @var Connection::socket
 * Socket file descriptor
 * 
 * @var Connection::address
 * Remote address
 * 
 * @var Connection::state
 * Current connection state
 */
typedef struct Connection {
    int socket;           /**< Socket file descriptor */
    Address address;      /**< Remote address */
    State state;          /**< Current state */
} Connection;

/* TODO and FIXME comments */

/* TODO: Implement retry logic */
/* FIXME: Handle edge case when buffer is full */
/* XXX: This is a temporary workaround */
/* HACK: Remove this after refactoring */
/* NOTE: Must be called before initialization */
/* WARNING: Not thread-safe */

/* Block comments for complex sections */

/*
 * Connection state machine:
 * 
 * DISCONNECTED -&gt; CONNECTING -&gt; CONNECTED -&gt; DISCONNECTING -&gt; DISCONNECTED
 *                      |              |
 *                      v              v
 *                   ERROR <-------&gt; ERROR
 * 
 * Transitions:
 * - connect(): DISCONNECTED -&gt; CONNECTING
 * - on_connect(): CONNECTING -&gt; CONNECTED
 * - disconnect(): CONNECTED -&gt; DISCONNECTING
 * - on_disconnect(): DISCONNECTING -&gt; DISCONNECTED
 * - on_error(): any state -&gt; ERROR
 */

/* Inline comments */

if (retries &gt; MAX_RETRIES) {
    /* Give up after too many attempts */
    return ERROR_TIMEOUT;
}

/* Avoid */
/* Comment out code with #if 0 instead of /* */
#if 0
    /* Old code here */
    old_function();
#endif

/* Not with nested comments: */
/*
 /* This won't work */
*/

/* Comment style consistency */

/* Single line: // or /* */ */
// Single line comment (C99+)
/* Single line comment (C89+) */

/* Multi-line: Always /* */ in C89 */
/*
 * Multi-line comment
 * with asterisk alignment
 */

/* Or without asterisks */
/* Multi-line comment
   without asterisk alignment */

/* License headers */
/*
 * Copyright (c) 2024 Company Name
 * 
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 * 
 * [Rest of license text...]
 */

/* Self-documenting code */

/* PREFER: Clear code over comments */

/* BAD */
int d;  /* elapsed time in days */

/* GOOD */
int elapsed_days;

/* BAD */
if (x & 0x01) { /* check if odd */ }

/* GOOD */
if (is_odd(x)) {"{ }"}

/* When to comment */
/*
   DO comment:
   - Why (rationale, design decisions)
   - Non-obvious behavior
   - Complex algorithms
   - Public APIs
   - Workarounds and limitations
   - TODO/FIXME items
   
   DON'T comment:
   - Obvious code
   - What code does (code shows this)
   - Outdated information
   - Commented-out code (use version control)
*/

Code Organization

Organize code logically: includes at top, constants, types, globals, static functions, public functions. Group related functions. Keep files focused. Separate interface (.h) from implementation (.c). Follow project structure conventions.

C
/* File organization template */

/* module.c */

/* 1. File header comment */
/**
 * @file module.c
 * @brief Module description
 */

/* 2. System includes */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* 3. Project includes */
#include "module.h"
#include "utils.h"
#include "config.h"

/* 4. Local includes */
#include "module_internal.h"

/* 5. Constants and macros */
#define MAX_SIZE 100
#define DEFAULT_VALUE 0

/* 6. Type definitions */
typedef struct InternalData {
    int field;
} InternalData;

/* 7. Static (file-scope) variables */
static int s_instance_count = 0;
static bool s_initialized = false;

/* 8. Forward declarations of static functions */
static void internal_helper(void);
static int internal_process(InternalData *data);

/* 9. Public functions (defined in header) */

/**
 * Public API implementation
 */
int module_initialize(void)
{
    if (s_initialized) {
        return -1;
    }
    
    /* Implementation */
    internal_helper();
    
    s_initialized = true;
    return 0;
}

void module_process(void)
{
    if (!s_initialized) {
        return;
    }
    
    /* Implementation */
}

/* 10. Static (private) functions */

/**
 * Internal helper - not visible outside this file
 */
static void internal_helper(void)
{
    /* Implementation */
}

static int internal_process(InternalData *data)
{
    /* Implementation */
    return 0;
}

/* Function ordering */

/* Option 1: Top-down (most common) */
/* main or high-level functions first */
/* helper functions later */

int process_data(void)
{
    prepare_data();
    transform_data();
    save_data();
    return 0;
}

static void prepare_data(void) { /* ... */ }
static void transform_data(void) { /* ... */ }
static void save_data(void) { /* ... */ }

/* Option 2: Bottom-up */
/* helper functions first */
/* main functions later */

static void prepare_data(void) { /* ... */ }
static void transform_data(void) { /* ... */ }
static void save_data(void) { /* ... */ }

int process_data(void)
{
    prepare_data();
    transform_data();
    save_data();
    return 0;
}

/* File size guidelines */
/*
   - Keep files under 1000 lines
   - Split large files by functionality
   - Each file should have single responsibility
*/

/* Directory structure */
/*
project/
├── include/           # Public headers
│   └── mylib/
│       ├── api.h
│       └── types.h
├── src/               # Implementation
│   ├── api.c
│   ├── network.c
│   └── utils.c
├── tests/             # Test code
│   ├── test_api.c
│   └── test_network.c
├── docs/              # Documentation
├── examples/          # Example code
└── CMakeLists.txt     # Build configuration
*/

/* Header file organization */

/* module.h */

#ifndef MODULE_H
#define MODULE_H

/* 1. Includes needed for interface */
#include <stddef.h>
#include <stdint.h>

/* 2. C++ compatibility */
#ifdef __cplusplus
extern "C" {
#endif

/* 3. Constants */
#define MODULE_VERSION 1

/* 4. Type forward declarations */
typedef struct Module Module;

/* 5. Enums */
typedef enum {
    MODULE_SUCCESS,
    MODULE_ERROR
} ModuleStatus;

/* 6. Public API */
Module* module_create(size_t size);
void module_destroy(Module *mod);
ModuleStatus module_process(Module *mod);

/* 7. Close C++ compatibility */
#ifdef __cplusplus
}
#endif

#endif /* MODULE_H */

/* Grouping related code */

/* Group 1: Initialization/Cleanup */
void module_init(void);
void module_cleanup(void);

/* Group 2: Configuration */
void module_set_option(Option opt);
Option module_get_option(void);

/* Group 3: Main operations */
int module_process(void);
int module_update(void);

/* Group 4: Utilities */
void module_debug_dump(void);
const char* module_version(void);

/* Separate concerns */

/* network.c - Only networking */
/* database.c - Only database */
/* ui.c - Only UI */

/* NOT: everything.c - Everything mixed */

Summary & What's Next

Key Takeaways:

  • ✅ Consistency is more important than specific style
  • ✅ Use descriptive names, avoid abbreviations
  • ✅ Choose brace style and stick to it
  • ✅ Comment why, not what
  • ✅ Use Doxygen for API documentation
  • ✅ Organize files logically and consistently
  • ✅ Use automated formatters (clang-format)
  • ✅ Follow project or team conventions

What's Next?

Let's learn about defensive programming and error handling!