C Coding Style

Table of Contents

General Guidelines
Syntax and Indentation
Commenting
Visibility Control

General Guidelines

Write in regular ANSI C whenever possible. Use the normal ANSI and POSIX constructs and use autoconf or portability wrappers to fix things up beforehand so that the code itself can read like regular ANSI or POSIX code. Code should be written so that it works as expected on a modern platform and is fixed up with portability tricks for older platforms, not the other way around. You may assume an ANSI C compiler and reasonable headers. Use memset instead of bzero, memcpy or memmove instead of bcopy, strchr instead of index, and in general always prefer the ANSI and POSIX C function names to any alternatives.

Try to use const wherever appropriate. Don't use register; modern compilers will do as good of a job as you will in choosing what to put into a register. Don't bother with restrict (at least yet).

Avoid #ifdef and friends whenever possible. Particularly avoid using them in the middle of code blocks. Try to hide all portability preprocessor magic in header files or in portability code in lib. When something just has to be done two completely different ways depending on the platform or compile options or the like, try to abstract that functionality out into a generic function and provide two separate implementations using #ifdef; then the main code can just call that function.

If you do have to use preprocessor defines, note that if you always define them to either 0 or 1 (never use #define without a second argument), you can use the preprocessor define in a regular if statement rather than using #if or #ifdef. Make use of this instead of #ifdef when possible, since that way the compiler will still syntax-check the other branch for you and it makes it far easier to convert the code to use a run-time check if necessary. (Unfortunately, this trick can't be used if one branch may call functions unavailable on a particular platform.)

Exported functions in libraries must always begin with a prefix that's a minimum of three characters followed by an underscore. The same is true of data types made visible in headers (except possibly for a data type with the same name as the prefix used for everything else), constants, and so forth. This is to prevent accidentally stepping on the namespace of another library.

The preference is to use plain structs, without a typedef, for abstract data types. Please try to avoid typedefs except for function pointers or other extremely confusing data types, or for data types where we really gain some significant data abstraction from hiding the underlying data type. Also avoid using the _t suffix for any type; all types ending in _t are reserved by POSIX. For typedefs of function pointer types, a suffix of _func usually works.

Checks for NULL pointers are preferrably written out explicitly; in other words, use:

    if (p != NULL)

rather than:

    if (p)

to make it clearer what the code is assuming.

Syntax and Indentation

The preferred indentation style roughly corresponds to that produced by GNU indent 2.2.6 with the following options:

    -bad -bap -nsob -fca -lc78 -cd41 -cp1 -br -ce -cdw -cli0 -ss -npcs
    -ncs -di1 -nbc -psl -brs -i4 -ci4 -lp -ts8 -nut -ip5 -lps -l78
    -bbo -hnl

Unfortunately, indent currently doesn't get everything right (it has problems with spacing around struct pointer arguments in functions, wants to put in a space between a dereference of a function pointer and the arguments to the called function, misidentifies some macro calls as being type declarations, and fouls up long but simple case statements).

At most 79 characters per line (and preferrably 78 so that patches are still under 80 columns). Split long strings between multiple lines using ANSI string concatenation. For functions with particularly long names and lots of arguments (such as many of the Kerberos API functions), consider indenting a continuation of the arguments four spaces farther than the start of the function name rather than all the way to the open parenthesis.

Roughly BSD style but with four-space indents. This means no space before or after the parens around function arguments, open brace on the same line as if/while/for, and close and open brace on the same line as else). Prefer a space between the cast and the variable being cast.

Indent nested preprocessor directives by one space, between the # and the directive, for each level of surrounding #if. For example:

    #if HAVE_SSL
    # if HAVE_OPENSSL_SSL_H
    #  include <openssl/ssl.h>
    # endif
    #endif

Any source file should start with the includes in three sections, separated by a single blank line: config.h and any portable/* headers (from rra-c-util), then any system or external software headers, and then any headers internal to this package. Use <> around all headers and never use "" for consistent search paths and predictable behavior.

A function definition should always have the return type of the function on the first line and then the function name starting in column one on the next line, and use an ANSI style definition. The opening brace of the function should then be on a line by itself. For example:

    int
    function(char *arg1, float arg2)
    {

The function name is in column one so that one can easily locate the definition of a particular function with grep ^function *.c.

It's better to always put the body of an if statement on a separate line, even if it's only a single line. In other words, write:

    if (p != NULL)
        return p;

and not:

    if (p != NULL) return p;

This is in part for a practical reason: Some code coverage analysis tools like purecov will count the second example above as a single line and won't notice if the condition always evaluates the same way.

Don't use tabs in source files, since they can expand differently in different environments. In particular, please try not to use the mix of tabs and spaces that is the default in Emacs.

The recommended Emacs configuration, suitable for including in your .emacs file, is:

    (setq c-basic-offset 4)
    (setq indent-tabs-mode nil)

The corresponding vim configuration, suitable for including in your .vimrc file, is:

    set tabstop=4
    set expandtab

Commenting

Introductory comments for functions or files, multiline comments in source, and single-line comments that are particularly important should be written as:

    /*
     * Introductory comment.
     * Possibly multiple lines.
     */

Every file should start with a comment in the following basic format:

    /*
     * One-sentence description of file.
     *
     * Optional longer description of file, which may go on into multiple
     * paragraphs if required.
     *
     * Written by Author <email>
     * Copyright YYYY, YYYY Copyright Holder
     *
     * See LICENSE for licensing terms.
     */

If the author and copyright holder are the same, those lines can be combined. If the copyright dates and holder name are long enough that the line has to be wrapped, intent each continuation line with four spaces.

Every function should have a comment before it saying what it does, and explaining its return value and side effects.

When noting something that should be improved later, add a comment containing "FIXME:" so that one can easily grep for such comments. If the project you're working on has a TODO file at the top level, also add an entry there for any FIXME comment you add.

Visibility Control

When writing shared libraries in particular (and as good practice in general), all symbols should have as limited of visibility as possible to reduce the chances of symbol conflicts and the resulting runtime chaos. This means as many functions as possible should be declared static, without reducing readability and good code organization into separate files.

However, it's not possible for all symbols to be static. Where this is not possible, internal symbols, such as functions from utility and portability libraries, should be defined with hidden visibility when supported by the compiler. This will not only cause them to be omitted from the exported symbols in a shared library but also will make shared library code more efficient since hidden symbols can be resolved at link time and don't need the same relocation machinery.

To do this, for each header file defining non-static functions, surround all function definitions with:

    /* Default to a hidden visibility for all util functions. */
    #pragma GCC visibility push(hidden)

and then:

    /* Undo default visibility change. */
    #pragma GCC visibility pop

The #pragma method is preferred since it adds less clutter. However, if necessary, one can instead include portable/macros.h from rra-c-util, which will handle ensuring that the GCC-specific __attribute__ keyword can be used and will be removed if the complier is not GCC. Then, add:

    __attribute__((__visibility__("hidden")))

to the definition of each function or symbol that isn't public.

All public header files defining visible symbols should also include portable/macros.h and then surround all function definitions with BEGIN_DECLS and END_DECLS, which disables C++ mangling of the function names and therefore allows the header files to be used by C++ compilers. This is best practice for private header files as well, although less important there.

Last spun 2022-02-06 from thread modified 2013-01-04