Naming

Variable names and static helper functions should use camelCase.

Types, enums and constants should use TitleCase.

Externally-accessible functions should have a TitleCase name with their namespace (e.g. tito) as an all-lowercase prefix.

Macros should use ALL_CAPS with underscores, but macros should be generally avoided.

Names should be descriptive, with the extent of their specificity relative to the size of their scope. Function-local variables need not specify their entire scope in their names, whereas externally accessible functions should be clear when used in many different contexts. With the exception of loop variables (e.g. i), tiny-scope variables with an obvious use (e.g. tmp in a swap), and namespace prefixes ubiquitous in the codebase (e.g. tito), names should be composed of english words, or in the case of very long names, common and unambiguous abbreviations. We should all be able to pronounce your names.

Macros

Macros should be generally avoided in favor of enums for integer constants, const variables for other constants, and small functions that are likely to be inlined for function-like macros.

Use enums for nontrivial (e.g. 0, 1, -1) integer constants instead of const variables, macros, or literals.

Avoid using #ifdef in functions, preferring instead to factor out the conditional code into a small function, putting the #ifdef around that whole function body and providing a no-op stub in the #else.

Always provide a comment at the #endif with a repetition of the condition.

#ifdef CONFIG_SOMETHING
// ...
#endif // CONFIG_SOMETHING

Blocks and Whitespace

Braces should follow the One True Brace Style: all control structures (with the exception of individual cases) should use braces, and opening braces should be on the same line as their structure heading, with a space between the heading and the brace. The exception to this is function definitions, which should have the opening brace on its own line, indented at the same level as the function heading. Closing braces should be on their own line, except when followed by while in a do loop or else or else if in an if structure.

Blocks should be indented 2 spaces (including the cases in a switch), and line continuations should be indented 4 spaces. Try not to have your lines exceed 80 characters. However, do not break strings, especially user-visible strings, as this makes them more difficult to search the code for.

Every statement should be followed by a newline. Having multiple statements on a single line doesn't save that much space, and is easy to misread as a single statement. Similarly, declare each variable in a separate declaration, as the binding of various type modifiers in C makes multi-variable declarations easy to misread.

All assignment operators, binary arithmetic, bitwise, and logical operators, all comparison operators, and the ternary conditional operator should have spaces on both sides. Use parentheses to be explicit about operation order. We shouldn't have to look up the precedence chart to read your code. Commas should have space after them and no space before them. Other operators should not have whitespace between them and their operands. When breaking a line at an operator other than the comma, the operator should start the second line.

Put a space after if, switch, case, for, do, and while.

Do not leave whitespace at the end of lines.

Commenting

Try to write code that does not require inline commenting, but if you need to do something non-obvious, provide a brief comment. Inline comments should be immediately before the code they describe, indented at the same level.

Provide a brief Doxygen comment for each function describing its purpose. Markdown formatting style is preferred. Usage examples here and in file-level comments are encouraged. If you have a file-level comment, do not list the file name, author, or other metadata. This metadata is already accessible through git.

Commenting out large blocks of code is discouraged, but if you really need to, use #if 0 ... #endif rather than /* ... */. It actually nests properly. But you can really do whatever you want, because you shouldn't check in blocks of commented-out code.

Do not include editor modelines (e.g. -*- mode: c -*-, vim: set filetype=c :) in files.

The search token for todo comments is TODO:.

Declarations

Do not use auto, inline, or register. Everywhere it is reasonable to have an auto-storage-class variable, the auto is implicit. inline and register, on the other hand, do nothing in modern compilers. Do use noreturn if your function unconditionally exits.

Function declarations should include the names of parameters, and should always include the return type.

Generally avoid declaring typedefs, especially to hide pointers. The exceptions to this are when you really do need an opaque object (which should be rarely), and when using function pointers. Function pointer syntax is weird, and using a typedef for them generally makes the code clearer.

Only declare global variables when you really need to.

Statements

Avoid goto and longjmp.

All switch statements should have a default label, even if it is just a false assertion. All cases in a switch that have any code exclusive to them should end with break. This means don't get cute with fallthrough.

Use assertions liberally.

Functions

From the Linux kernel coding-style.rst:

Functions should be short and sweet, and do just one thing. They should fit on one or two screenfuls of text (the ISO/ANSI screen size is 80x24, as we all know), and do one thing and do that well.

The maximum length of a function is inversely proportional to the complexity and indentation level of that function. So, if you have a conceptually simple function that is just one long (but simple) case-statement, where you have to do lots of small things for a lot of different cases, it's OK to have a longer function.

However, if you have a complex function, and you suspect that a less-than-gifted first-year high-school student might not even understand what the function is all about, you should adhere to the maximum limits all the more closely. Use helper functions with descriptive names....

Another measure of the function is the number of local variables. They shouldn't exceed 5-10, or you're doing something wrong. Re-think the function, and split it into smaller pieces. A human brain can generally easily keep track of about 7 different things, anything more and it gets confused. You know you're brilliant, but maybe you'd like to understand what you did 2 weeks from now.

If the success or failure of a function is relevant to the caller, the function should return a boolean success value, unless it would otherwise return some other value in which case it should return some invalid value (e.g. NULL, -1).

Use early return, continue, and break. That is, do

static bool doSomething() {
    if (!conditionTrue()) {
        return false;
    }
    char *resource = loadResource();
    return doSomethingElse(resource);
}

rather than

static bool doSomething() {
    if (conditionTrue()) {
        char *resource = loadResource();
        return doSomethingElse(resource);
    }
    return false;
}

Additional Reading

The Linux kernel's 4.Coding.rst is probably a good read, even though some of it is specific to kernel development.