C style guide

nickbjohnson4224 edited this page Apr 6, 2011 · 7 revisions

All C code in the Rhombus core distribution (kernel, libc, basic drivers, core utilities) is subject to the following style guide. Anything outside of this is not required to follow this guide, but it is recommended for new code written specifically for Rhombus to follow it. The most important thing is to have code consistently formatted within a particular project or subsystem.

If anything here is unspecified, fall back to the FreeBSD Kernel Normal Form style, found here: http://www.freebsd.org/cgi/man.cgi?query=style&sektion=9&manpath=FreeBSD+5.0-current.


Indents and Control Statements

For all source files, including non-C files, the soft and hard tab widths are set at 4 spaces; if possible, use tabs instead of spaces for alignment of code blocks.

Control statements requiring curly brace blocks should have the opening brace on the same line as the statement, separated from the statement with a single space:

statement {
    ... code ...
}

function(arg, arg2) {
    ... code ...
}

Control statements with parenthesis-enclosed statements, such as if and while, should have a space between the statement and the opening parenthesis:

statement (expression) {
    ... code ...
}

Else statements, unlike in KNF style, should be on a separate line from the closing brace of the previous if statement:

if (expression) {
    ... code ...
}
else {
    ... code ...
}

Else statements may have if statements immediately following them forming a sort of "else if" statement on a single line:

if (expr) {
}
else if (expr) {
}
else {
}

Statements that can use curly-brace enclosed blocks should use them, even if there is only a single statement in the block, unless that statement is a return statement for a single value, which may be inlined:

// bad
if (expr) statement;

// good
if (expr) {
    statement;
}

// also acceptable
if (expr) return value;

// but this is not:
if (expr) return (long expr);

Expressions

All binary arithmetic, comparison, and assignment operators other than ++ and -- must be spaced from their operands by a single space. The operands, however, do not have to be spaced from parentheses. Unary operators, such as ~ and !, do not have to be spaced:

// bad
x-=(1+2)*~0xF;

// good
x -= (1 + 2) * ~0xF;

// but still okay
x++;

// bad
if (!(x==(y+1))) {
}

// bad
if ( ! ( x == ( y + 1 ) ) ) {
}

// good
if (!(x == (y + 1))) {
}

Names and Type Names

Function and global variable names should be in lower case, using underscores to separate parts of names. Static functions and static global variables are recommended to have a single underscore at the beginning of their name, especially if the name could conflict with another in the future. Functions declared in header files that are not intended for use outside of the subsystem of the program or library that the header belongs to should have two underscores at the beginning of their name.

Typedefs should be used sparingly, especially for structures. If possible, always use the "struct structname" type format when using structures instead of typedef'ing to "structname_t" or similar.

When a single structure is the central part of a subsystem, it is recommended to name that structure similarly to the subsystem. In addition, functions that operate upon that structure should then be prefixed with the name of the structure followed by an underscore. This essentially makes a namespace for the subsystem.


Functions

Called functions and function declarations must not have a space between the function name and the opening parenthesis; arguments must be spaced, but not spaced from the enclosing parentheses. Return types of function declarations must be on the same line as the rest of the prototype. Prototypes in header files may have spacing for alignment purposes.

// bad
int
func (arg,arg2) {
}

// good
int func(arg, arg2) {
}

// only in headers
int  func123(arg, arg2);
char func2  (arg);

Functions should declare all variables in a contiguous block at the top of the function body that is separated from any further code by a single empty line:

int func() {
    int var1;
    int var2;

    ... code ...
}

If no variables are declared in a function, there must still be a space between the function prototype line and the remaining code:

// bad (but not that bad -- for very short functions, like one-liners, this is fine)
int func() {
    ... code ...
}

// good
int func() {

    ... code ...
}

In general, lines of code that are related should be grouped into blocks separated from other blocks by empty lines. For example (from the current kernel code, with added comments):

uintptr_t thread_bind(struct thread *thread, struct process *proc) {
    uintptr_t i;
    uintptr_t addr;

    // block 1
    for (addr = 0, i = 0; i < MAX_THREADS; i++) {
        if (!proc->thread[i]) {
            proc->thread[i] = thread;
            addr = SSPACE + (SEGSZ * i);
            break;
        }
    }

    // block 2
    thread->id = i;
    thread->stack = addr;
    thread->proc = proc;

    // block 3
    return addr;
}

Commenting and Blocks

When commenting inside functions, place single-line comments at the beginning of blocks. If an individual line needs to be commented, make it its own block before doing so. Do not put comments on the same line as code:

// wrong
void func() {

    line1; // this is an awesome line
    // this is a cool line
    line2;
}

// right
void func() {

    // this is an awesome line
    line1;

    // this is a cool line
    line2;
}

Single line comments can be either C++ or C style (i.e., they can start with // or be delimited by /* and */); multiple line comments, however, must be C++ style. This is because it is impossible to comment out multiple lines with C style comments if those lines contain other C style comments.

Multiple-line comments inside functions may be of the following form:

/*
 * This is a multi-line comment.
 * Please make it multiple lines.
 */

There are also multi-line comments used to document external behavior of functions and global variables. These should appear immediately before the function definition. This style of comment may also be used to write abstracts for entire files. There are 78 asterisks on the first line, to make it 79 characters wide.

// there are 78 asterisks on the first line, to make it 79 characters wide
/****************************************************************************
 * function_name (or global variable name)
 *
 * Function description: write this in full sentences. This should describe
 * all relevant behavior of the function for anyone who wants to use it.
 *
 * After the description, general notes may be made in further paragraphs.
 * For very specific stuff, though, put comments in the function body.
 */

int function_name(arg1, arg2) {
...

To separate a file into different sections, use the following comment format. It is exactly 79 characters wide no matter what. Pad it with asterisks. This is used most often in header files, but can be used in C files.

/* section name **************************************************************/

Macros

All macro constants and functions should have names in all caps, using underscores to separate parts of the name, unless doing so violates some standard:

// bad
#define macro 2

// bad
#define Macro 2

// good
#define MACRO 2

// okay, because of standard
#define getc() (fgetc(stdin))

Macro functions should be used sparingly, especially in situations where they would cause multiple evaluation of arguments:

// be careful of these
#define MACRO_FUNC(a) ((a) + (a))

// because of this
MACRO_FUNC(x++);
->
((x++) + (x++));