there is no place like ~

A modern C coding guideline

Description

There are plenty of Coding Guidelines out there in the Web, but many of them have one fundamental flaw: they are old. They were written many years ago when C99 was very new or did not even exist yet. C99 is reasonably seasoned now and most of the compiler vendors have adopted it, and apart from very particular cases there is no reason anymore to advocate against C99. This guide lines have been heavily influenced by the following guides: Python's Style Guide for C Code, the JSF air vehicle C++ coding standard and some styles in the POSIX API (especially the naming conventions).

Definitions

The key words must, must not, required, shall, shall not, should, should not, recommended, may, and optional in this document are to be interpreted as described in RFC 2119.

C dialect

  1. The use of ISO C99 is recommended.
  2. The new C99 type boolshall be used on all logic operations and the fixed-size types like uint8_tshould be used whenever an exact size of a variable is required. For types with a minimum size requirement use types like uint_least8_t or uint_fast8_t. The new types do increase portability and can be easily redefined for compilers that do not support them.
  3. Proprietary compiler extensions should not be used.
  4. All function declarations and definitions must use full prototypes (i.e. specify the types of all arguments).
  5. C++ style // one-line comments may be used.
  6. The code must compile without compiler warnings. The warning level should be set on a reasonably strict level (e.g. -W -Wall and possibly -Werror with GCC).

Code Layout

  1. The code must be properly indented. It is recommended to indent with 4 spaces. Tab stops must not be used.
  2. Lines should be kept shorter than 120 characters.
  3. Lines should not end in white space. Trailing white spaces may be deleted automatically by other team members, and tend to lead to unnecessary changed lines in a file.
  4. Automatically expanded text (such as e.g. Version Control Keywords) should be kept to a minimum. They cause differences when comparing two source trees and can to generate conflicts when merging versions of the same file.
  5. A function definition should be kept in one line if the the function accepts one parameter. If the function accepts more than one parameter, then the function definition should be split, with the first parameter in the same line as the function name and each one of the following parameters on a separate line.
  6. One space shall be placed between keywords like 'if', 'for', etc. and the following left parenthesis; no spaces inside the parenthesis, braces as shown:
    if (ptr != NULL)
    {
        ...
    }
    else
    {
        ...
    }
    
  7. The statements forming the body of an if, else, for, etc. shall always be enclosed in braces, even if the braces form an empty block.
  8. The return statement should not get redundant parentheses:
    return NULL;     // OK
    return (NULL);   // less readable
    
  9. Function and macro calls shall not contain space before the opening parenthesis no spaces inside the parenthesis, no spaces before commas, one space after each comma. e.g. foo(a, b, c);.
  10. Long lines should be split after commas in the outermost argument list. Continuation lines shall be indented appropriately.
  11. When breaking a long expression at a binary operator, put the operator at the end of the previous line, e.g.:
    if (type->tp_dictoffset != 0 &&
            base->tp_dictoffset == 0)
    {
        return 0;
    }
    
  12. Put blank lines around functions, structure definitions, and major sections inside functions.
  13. Comments shall go before the code they describe.
  14. All functions and variables should be declared static unless they are to be part of a published interface.
  15. External functions and variables must be declared in an appropriate header file. Use the external keyword only where necessary; it is not needed for function declarations and should be best avoided.

Naming conventions

  1. Identifiers shall be written in lowercase and should contain an underscore _ between words.
  2. Macros and enumeration identifiers shall be written in uppercase letters to distinguish them from normal identifiers. A notable exception to this rule are macros that behave like functions but cannot be expressed as static inline function. The assert() macro is an example.
  3. User defined types shall end with _t. Structures, enumerations and unions are marked by the struct, enum and union keywords and do not need additional markings. Instances (variables) of the types defined above don't have a suffix.
    typedef unsigned int counter_t;
    
    struct iq_data
    {
        int i;
        int q;
    };
    typedef struct iq_data iq_data_t;
    
    enum error
    {
        NO_ERROR,
        ERR_OUT_OF_MEM,
        ERR_NOT_IMPLEMENTED,
        ERR_NOCLUE,
    };
    
    counter_t count;
    iq_data_t a, b;
    enum error error;
    

Programming conventions

  1. Debug versions of the code should use the assert() macro extensively. This helps to spot some programming errors when they occur. In debug releases the program execution will halt on the wrong assertion, allowing the programmer to inspect local variables.
  2. Assertions shall not be used as error recovery or additional run-time checks. Release code must not contain any code related to assertions.
  3. A header file shall use include guards.
  4. Header files should include only the minimum set of .h files to compile it without warnings.
  5. Strong typing is highly recommended. Use size_t for objects that describe a length, define your own types for particular objects. This gives the compiler the opportunity to detect programming errors at compile time and will help you refactoring the code when needed.