C Coding Style Guide (Version 1.0.4)

For a compact summary, see the C Coding Style Cheat Sheet.

1. General Rules

All comments must be in English. Function and variable names must also be in English.

Indentation uses tabs. A tab counts as 4 spaces when it must be counted as spaces.

Tabs are used strictly for indentation. For alignment, use spaces.

2. Includes and Header Structure

Use "#pragma once" to avoid duplicate includes.

Include project headers before external library headers, and external headers before system headers. Leave one blank line between those sections.

The same spacing rules apply in header and implementation files. Leave two blank lines between different kinds of declarations or definitions, for example between includes, type definitions, and function declarations or definitions.

Examples:

#include "data.h" // Project
#include "config.h" // Project

#include "moduleA/file.h" // External lib
#include "moduleB/array.h" // External lib

#include <stdint.h> // System
#include <stdlib.h> // System

3. Line Breaking

Lines must not exceed 80 characters. The line break itself counts as a character, so the last visible character may only occupy column 79.

When a single expression must be broken, indent the continuation line. When broken again, do not indent again, unless this time a sub-expression is broken.

Break before mathematical operators (+, -, *, /, %) and logical operators (&&, ||, &, |, ^, <<, >>).

Break after assignment operators (=).

For assignment expressions, prefer breaking after = when both are possible.

For example:

int value =
	(getSomeFunctionOutput(input) + someOtherValue) * someFactor;

Break before comparison operators (==, !=, <, >).

Break before reference operators (. and ->).

When breaking at an opening (, [, or {, break directly after it.

Never reduce indentation by more than one level at once.

When a bracketed expression or braced construct is broken, place the closing ] or } on its own unindented line.

In a broken parenthesized sub-expression, keep the closing ) on the final expression line if the expression starts beside the opening (. If the expression starts on the following line, place ) on its own unindented line.

When a broken construct starts a {-block, place ) at the end of its final line and place { on the next line.

Examples:

a = b
	+ c
	+ d;

a = x + (
	a + b
	+ c
);

veryLongName =
	very long expression;

isEqual = (
	value1 == value2
);

item = entries[ selectedIndex + offset];

item = entries[
	selectedIndex + offset
];

total = (firstValue + secondValue
	- adjustment);

value = ptr
	->subPtr
	->subPtr2;

value = otherValue
	.field
	.subField;

a = fetchValue(
	source
) + c;

a = fetchValue(
	getSource(
		srcPtr
	).value
);

if (
	a == b
	&& c == d
	&& (
		e || someFunction(
			someArgument
		)
	))
{
	// code
}

4. Preprocessor Macros

Preprocessor directives always start at zero indentation, regardless of surrounding code.

Nested #if/#ifdef blocks are indented like code.

Examples:

		statement;
#if XXX
		statement;
	#if XYZ
		statement;
	#endif
#endif

Function-like macros must be defined and invoked with the same spacing conventions as regular functions.

When breaking macros across multiple lines, align the backslashes. Place at least one space before each backslash. Ignore the last line.

Examples:

#define XXX              \
	statement;           \
	very_long_statement; \
	last_statement_even_longer;

5. Types, Variables, and Constants

Use const for true constant values instead of preprocessor defines. Constant names must be all uppercase with underscores between words. True constants must be defined at file scope, never inside functions.

Also use const wherever possible for variables, but those use camel case.

static remains on the same line when it applies to a variable.

Names of output variables must always start with out.

Declare variables at first use when possible, and always initialize them. If a variable must be declared before its value is known, initialize it to a safe default.

If the same literal value is used in more than one place, define a named constant for it instead of repeating the literal.

Examples:

const int MAX_USERS = 1024; // True constant

const time_t timeNow = time(NULL);

for (size_t tableIndex = 0; tableIndex < MAX_USERS; tableIndex++) {

Do not expose variables and constants outside the current module (no extern). Use getter functions so external code can only obtain those values through function calls.

Avoid signed types unless negative values are required.

Only use char for character values, not for byte sized ints. Do not assume whether char is signed or unsigned; for character data this does not matter.

Enum names and structure names start with uppercase.

Place { on the same line as the enum/structure definition. Put each enum value or structure field on its own line.

Suffix enum values with enum name and underscore. The last enum value must also end with a comma.

Examples:

enum EName {
	Value1_EName = 1,
	Value2_Ename,
	Value3_Ename,
};

Structures can be defined on a single line if they are simple (no pointers, no nested structures/unions) and short (at most 5 fields) and still fit within the line length limit.

Leave one blank line between adjacent multi-line structure definitions. Adjacent single-line structure definitions do not need a blank line between them.

Examples:

struct Coordinate { float x; float y; };

struct Complex {
	// many fields
};

When initializing simple structs, fields don't need to be named. For complex structs, name fields and place a trailing comma after the last field. Fields that should be zero/NULL may be omitted.

Examples:

struct Coordinate coord = { 0 };

struct Coordinate coord = { 1, 2 };

struct Complex cmplx = {
	.field1 = value1,
	.field2 = value2,
	.field3 = value3,
	// All other fields are zero/NULL
};

Do not typedef every structure and enum into the global namespace. Only typedef opaque types and enums used as options; keeping namespaces separate is often advantageous.

Leave one blank line between adjacent multi-line typedefs. Adjacent single-line typedefs do not need a blank line between them.

6. Pointers and Arrays

Place spaces around * when used as multiplication or in pointer declarations, but not when used for dereferencing.

Examples:

int a = b * c;
uint8_t * ptr = ...;
uint8_t value = *ptr;

Function pointers must always be assigned using the & operator.

funcPtr = &func;

When defining an array without fixed bounds, place a space between [ and ]. Also place a space between { and } in initializers.

Examples:

int a[ ] = { 1, 2, 3, 4, 5 };

When declaring function parameters:

7. Functions

Functions with external linkage start with an uppercase letter. File-local functions start with a lowercase letter.

Do not put a space between a function name and its opening parenthesis. Calls also have no spaces inside their parentheses; declarations and definitions do.

Leave one blank line between adjacent function declarations. This applies in both header and implementation files. Two blank lines separate groups of function declarations.

Examples:

int GetValue( );

void addValue( struct Array * ar, const void * value );

int sumUp( int values[], size_t count );

Keep function calls, declarations, and definitions on one line when they fit.

If one must break, break directly after the opening parenthesis and start the argument or parameter list on the following line. Split the list only when necessary. Use up to three arguments or parameters per line for no more than two lines. If the list requires more than two lines, place one argument or parameter per line.

In a broken call, place the closing ) after the final argument. In a broken declaration, place the closing ) on its own unindented line.

Examples:

matchedRecord = FindMatchingRecordInCollection(
	availableRecords, availableRecordCount, requestedIdentifier);

bool FindMatchingRecordInCollection(
	const struct Record records[],
	size_t recordCount,
	const char * requestedIdentifier,
	bool includeArchived,
	size_t startIndex,
	size_t maximumResults,
	struct Record * outRecord
);


static
struct Record * findMatchingRecordInCollection(
	const struct Record records[], size_t recordCount,
	const char * requestedIdentifier )
{
	// body
}

Function attributes, including static, are placed on their own line above the function definition.

Between the closing brace of a function and the next statement there are two blank lines. Three blank lines separate groups of functions. Inside functions, use at most one blank line between instruction groups. Add grouping comments when blank lines alone do not make the groups clear.

Examples:

void func1( int a, int b );

inline
int func2( int a, int b )
{
	// body
}

static int x = 10;

static
void printValue( int x )
{
	// body
}

int c = func2(20, 30);

8. Control Flow

Place { on the same line as if, for, while, etc. whenever possible.

Do not use braces for single-statement branches unless the statement is broken across lines.

An if with an else must use braces for both branches, even when each branch contains only one statement.

Use braces for multi-statement branches.

If a control structure must be broken across lines, it follows the broken block-head rule from section 3: place ) at the end of the final condition line, and place { on the next line.

Examples:

if (cond) singleStatement;

if (cond) {
	singleStatement;
} else {
	otherSingleStatement;
}

if (condition1
	&& condition2
	&& condition3)
{
	singleStatement;
}

if (cond) {
	// multiple statements
}

for (init; test; each) singleStatement;

for (init; test; each) {
	// ...
}

for (
	init;
	test;
	each)
{
	// ...
}

Break while at the end of a do-while loop just as you would break a function call.

Exception: a single line may contain two statements only if the second is a control flow statement (break, return, goto).

Examples:

if (cond) { doSomething(); break; }

Prefer early returns if possible.

In a large code block with multiple early returns, leave a blank line after each early return before the next instruction group.

Avoid goto except for clean up purposes.

Switch statements:

Indent every case as well as every case body. Always use { and }, unless the statement fits a single line and doesn't require new stack variables.

Each case must end with break, return, or goto. If fallthrough is intended, it must be documented with a comment, unless using a fallthrough statement is already required.

Examples:

switch (x) {
	case 1: statement; statement; break;
	case 2: statement; statement; // fallthrough
	case 3: statement; statement; return;
}

switch (x) {
	case 1: {
		statement;
		statement;
		break;
	}

	case 4:
	case 2: {
		statement;
		statement;
		// fallthrough
	}

	case 3: {
		statement;
		statement;
		return;
	}
}

Return may contain only a single value. If it contains an expression, wrap the expression in parentheses.

Examples:

return 10;

return a;

return getValueFrom(x);

return (a + b);

return (getValueFrom(x) >> 3);

9. Expressions and Operators

Always use parentheses around ==, <, >, <=, >= when its result is part of a larger expression, but omit them if the expression is already enclosed in braces.

Examples:

b = (a == b);
b = x && (a == b);
if (a == b) { // ...

Use parentheses to clarify operator precedence, even if not strictly necessary.

Never put a space between a cast and the expression it casts.

Correct forms include:

(size_t)-1
(char *)(ptr + 8)

Examples:

a = (b * c) + d;
a = d || (b && c);
if ((a == b) && (c == d)) { // ...

Ternary operators:

Examples:

x = testSomething() ? valueIfTrue : valueIfFalse;

x = testSomething() ?
	valueIfTrue : valueIfFalse;

x = testSomething() ?
	valueIfTrue
	: valueIfFalse;

doSomething(a, b, ( c > d ? c : d ));

10. Strings

When breaking string literals, place the space before the line break.

Examples:

"abc def "
	"hij klm"

11. Comments

Use // for comments. Use /* ... */ only when a comment must be mid-line.

Prefer comments above code lines to comments at the end of a line if the comment refers the line as a whole. Prefer comments at the end of line if the comment refers to an assigned value.

Documentation comments use /** ... */ and are placed before functions. Lines inside documentation comments are not prefixed with * but are indented.

Examples:

// Prefer this
statement; // over that

// That is, unless comment explains assigned value
speed = 100; // 100 Mbit/s

/**
	This is a documentation comment for the function below.
*/
static
void function( ... )
{
}

Normal comments do not need to form full sentences.

If a normal comment is a single sentence, omit the final punctuation. If it consists of multiple sentences, end each sentence with a punctuation character, including the last one.

Documentation comments must always form full sentences.

Use @param when the parameter's purpose, valid values, ownership, or constraints are not obvious from its type and name alone. Use [in] and [out] qualifiers only when the function has at least one output parameter. Omit these qualifiers when all parameters are inputs.

Indent continuation lines for @param descriptions.

Prefer @returns over @return. Indent continuation lines for inline @returns descriptions.

When @returns has no inline description, it marks a block. The block starts on the following line without continuation indentation and may contain multiple paragraphs. Indent lists inside the block for readability.

Use backticks for code identifiers and fenced blocks (\\\`) for code samples. Markdown emphasis using _ and ** is permitted.

Example:

/**
	Compresses one complete input buffer into an output buffer.

	@param[in] input The bytes to compress. This may be NULL only when
		inputSize is zero. The input and output buffers must not overlap.
	@param[in] inputSize The number of input bytes.
	@param[out] outputLength Receives the number of bytes written. This
		pointer must not be NULL.

	@returns
			- Success_SquinchResult: On success.
			- InvalidArgument_SquinchResult: For invalid arguments.
			- OutputBufferFull_SquinchResult: When output is insufficient.
*/

/**
	Returns the number of bytes required to store the value.

	@param value The value to measure.

	@returns The number of bytes required to store `value`, including
		the terminating byte.
*/

/**
	Reads data from the file.

	@returns
	The number of bytes read.

	Errors are reported through the returned negative result.
*/

Use grouping comments to group blocks of code if this is required for better readability.

Section markers are optional. Use them when a file contains large sections that benefit from a clear visual separation:

// ------------------------------------------------------------------------
// MARK: Section Name

Keep two blank lines before a marker and one blank line after it. The marker uses the normal indentation of the surrounding code. It may also be placed at the top and bottom of the entire file to mark its boundaries.

12. Consistency and Readability Win

Sometimes two or more adjacent blocks of code are closely related or perform the same operations but, due to different identifiers, would be formatted differently under the rules. In these cases, consistency takes priority, so the code forms a clear visual pattern.

For example, it is acceptable to add line breaks or adjust formatting, even when not strictly required, to keep similar statements aligned or to have the same line layout across multiple blocks.

Readability outweighs strict rule-following!