Title: C Language Coding Standard

 

 

Revision History

 

Rev         Description                                                                Author                  Approved           Date

1.0

Initial release

M. Motwani

M. Motwani

2/1/02

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

1.       Introduction................................................................................................................ 1

2.       General............................................................................................................................ 1

2.1    Keep it Simple................................................................................................................... 1

2.2    Maintainability.................................................................................................................. 1

2.3    Table-driven Logic............................................................................................................ 1

2.4    Compiler Warnings........................................................................................................... 1

2.5    Portability.......................................................................................................................... 1

2.6    Pointers.............................................................................................................................. 1

2.7    Test Code.......................................................................................................................... 1

2.8    Consistency....................................................................................................................... 2

2.9    Conserve Resources......................................................................................................... 2

3.       Code.................................................................................................................................... 2

3.1    Statement Format.............................................................................................................. 2

3.1.1   Line Format.................................................................................................................. 2

3.1.2   Braces.......................................................................................................................... 2

3.1.3   Blank Lines and Form Feeds......................................................................................... 2

3.1.4   Spaces and Indenting..................................................................................................... 2

3.1.5   One Statement – One Line............................................................................................ 4

3.2    C File Organization........................................................................................................... 4

3.3    Header Files...................................................................................................................... 5

3.3.1   Including Header Files................................................................................................... 5

3.3.2   Header File Organization............................................................................................... 6

3.3.3   Text Header Files.......................................................................................................... 6

3.4    Naming Conventions......................................................................................................... 6

3.5    Self-Documenting Code.................................................................................................... 6

3.6    Structured Programming................................................................................................... 8

3.7    Functions............................................................................................................................ 9

4.       Comments....................................................................................................................... 9

4.1    Identification and Modification History......................................................................... 10

4.2    Function Headers............................................................................................................ 10

4.3    Section Headings............................................................................................................. 10

4.4    General Documentation.................................................................................................. 11

4.5    Documenting Data.......................................................................................................... 11

4.6    Comment Format............................................................................................................. 11

4.7    Comments in .C versus .H Files..................................................................................... 11

4.8    Summary.......................................................................................................................... 11

 


1.     Introduction

The purpose of this standard is to assist in producing quality, maintainable C code.  Some sections deal with general coding practices and approaches.  Others deal with specific rules for producing uniform-looking code.  No standard, however, can substitute for clear requirements, good design, and careful implementation.

 

This document is not a tutorial and it does not explain the C language.  It assumes you know how to write in C and that you either know or will look up standard C terminology.

 

 

2.     General

 

2.1     Keep it Simple

Always look for the simplest, most straightforward solution to a problem.  Each C file should have a single, clear purpose.  Every line in the file should support that purpose.  Do not put special code or "hooks" in one file to perform unrelated functions that belong in another file.  A .C file should normally contain one function or several closely related functions and related data.  For example, a .C file might contain the functions that support a real-time clock or a bonus game.  Try to limit source files to about 1,000 lines.

 

2.2     Maintainability

Try to write code that is easy to maintain.  If you know your code may need certain new features in future releases, try to facilitate those changes. It is often beneficial to add a few lines of code to make a module much more general.  However, do not add lots of code for which no requirement exists.  Do not write grandiose routines that attempt to provide every feature you can imagine, when something much simpler will do.  Remember, you must debug, test, and document all of your code, including what you add in anticipation of future changes.

 

2.3     Table-driven Logic

Table-driven logic is sometimes easier to understand and maintain than the equivalent logic implemented as a series of control statements.  Table-lookups nearly always execute faster.  Try to use table-driven logic in those situations where it is appropriate.

 

2.4     Compiler Warnings

Compile with all appropriate warnings on.  Compiler warnings usually indicate sloppy coding.  They often point to actual or potential bugs.  If possible, check your code for errors and warnings with a second compiler.  Different compilers often detect different problems.  Try to fix all warnings the compilers report.

 

2.5     Portability

In the interest of portability, try to avoid writing code that makes assumptions about the byte order or sizes of the various data types.  If you need to make such assumptions, document the fact that it isn't portable.  If possible, use conditional compiles to generate correct code in all possible cases.  If this isn't possible, you may be able to cause the compiler or linker to generate a warning or error if your assumption is incorrect.

 

2.6     Pointers

The use of pointers is a powerful, but dangerous tool.  If we corrupt a data value, we corrupt just that variable.  If we corrupt a pointer, we could end up corrupting anything, including return values on the stack.  A corrupted function pointer causes wild jumps, which can cause all kinds of symptoms and be very hard to diagnose.  The use of pointers, especially function pointers and pointers to pointers, can complicate debugging.  Use pointers wherever they are appropriate, but don't use them needlessly.  Be aware of the risks and always use pointers carefully.

 

2.7     Test Code

Released code should have little, if any, test or debugging code.  Put conditionals, preferably #ifdef DEBUG, around test code.  Remove test code that isn't likely to be understood and used by others in the future.

 

2.8     Consistency

Never declare the same thing in two different files, even if the declarations are the same.  This eliminates the possibility that someone will change one declaration and not the other.  The exception is that low-level .C files may un-define a macro and replace it with a special local version.  Be careful if you do this.

 

2.9     Conserve Resources

Use resources wisely.  Do not waste code space, data space, or machine cycles.  Do not lock out other tasks longer than necessary.  Be aware of the impact your code may have on the resources of the system.

 

 

3.     Code

 

3.1     Statement Format

Code that follows a standard format is easier to recognize, understand, and maintain. This section sets out rules for writing neat, uniform-looking code.

 

3.1.1     Line Format

Try to fit each line within 80 columns.  There should be no trailing spaces at the end of a line.  Use spaces to indent code.  Do not use the tab character (0x9) in source code. (You may use the tab key in your editor; just make sure the editor does not store tab characters in the source file.)

 

3.1.2     Braces

Braces belong on separate lines, in the same column as the first character of the line preceding the first brace.  These abbreviated forms are OK for simple enumerations and for initializing arrays and structures:

Typedef enum { SUN, MON, TUE, WED, THU, FRI, SAT } Day_Of_Week;

COMMAND accel_3[] = { ACCEL4, {3, 1}, {3, 0} };

 

3.1.3     Blank Lines and Form Feeds

Use one blank line to separate sections of code in a function, groups of variables, etc.  Use two blank lines or a form feed to separate functions and other major components of a source file.

 

3.1.4     Spaces and Indenting

Indent code between braces four spaces.  Put the statement following an if on a separate line and indent it four spaces.  Do the same with the statement following an else or else if, the body of a for, while, or do-while loop, and the case and default labels of a switch statement.  Put the statements following case or default labels on separate lines and indent four additional spaces.

 

Use one space in these places (except at the end of a line):

·         Between the keyword for, if, switch, or while and the opening parenthesis.

·         In function definitions (but not prototypes or function calls) between the name of the function and the opening parenthesis.

·         After a comma of semicolon.

·         Before and after the binary operators: +, -, *, /, %, <<, >>, &, ^, |, ==, !=, &&, ||, <, >, <=, and >=.

·         Before and after the assignment operators: =, +=, -=, *=, /=, %=, <<=, >>=, &=, ^=, and |=.

·         Before and after the question mark and colon when they are ternary operators.

 

Do not use spaces in these places:

·         Between the function name and the opening parenthesis in function calls and prototypes.

·         Between a unary operator (-, +, ++, --, !, ~, &, and *) and its operand.

·         Before or after the opening bracket or before the closing bracket of a subscript.

·         After an opening parenthesis or before a closing parenthesis.

·         Before or after the member selection operators: . (period) and ->.

·         Before the comma, semicolon, and colon (except when the colon is a ternary operator).

 

This example shows how to indent code and where to put spaces in code:

unsigned int my_function(char *ptr);

 

typedef struct

{

    unsigned int val;

    char *ptr;

} My_Struct;

 

typedef union

{

    unsigned long value;

    My_Struct structure;

} My_Union;

 

static My_Struct my_struct =

{

    37,

    "Hi there!"

};

static My_Struct *my_ptr = &my_struct;

 

 

void main (void)

{

    unsigned int i, j = 0;

    char string[] = "Hello world!\n";

 

    for (i = 0; i < sizeof(string); i++)

    {

        if (i & 2)

        {

            j = my_function(&string[i]);

            j--;

        }

        else if (i > 7)

            j = my_function(my_ptr->ptr);

        else

            j++;

    }

 

    switch (j)

    {

        case 0:

            i = +8;

            break;

 

        case 2:

            i >>= 1;

            break;

 

        case 9:

        default:

            i *= 5;

            break;

    }

 

    while (j)

        j = my_function(string + j);

 

    do

    {

        i--;

    } while (i);

}

 

In general, code should not contain two or more consecutive spaces except when indenting as described in this standard.  However, it sometimes aids readability to indent enumerations, etc. to form a table.  For example:

Typedef enum

{

    red =    0x99,

    blue =   0xFF,

    purple = 0x23

} Color;

If you use this technique, be sure to do it neatly and consistently.  Haphazard, inconsistent, indenting is a distraction, not a benefit.

 

Enclose the operand of the sizeof operator in parentheses, without a space before the opening parenthesis.

 

3.1.5     One Statement – One Line

Each statement belongs on a single line unless there is a need to split it into two lines.  Do not put multiple statements on the same line.  Except for minor local variables, declare only one variable per statement.  For example:

unsigned int signal_length;

unsigned int num_coeff;

signal_length = 0;

num_coeff = 7;

is better than

unsigned int

    signal_length,

    num_coeff;

signal_length = 0;  num_coeff = 7;

 

If a line of code does not fit in 80 columns, you must either extend it a few columns beyond 80 or, preferably, break the line into two or more pieces.  Try to break the line at a logical point, and indent the continuation lines in a logical manner.  If possible, indent the continuation lines so that related information on successive lines appears in the same columns.  Otherwise, indent the continuation lines four spaces.

 

3.2     C File Organization

Arrange the contents of .C files in this order:

·         Version control header.

·         File identification and modification history.

·         #include commands, with conditionals, if required.

·         #define commands.

·         typedef declarations.

·         Prototypes for static functions.

·         Global and static data declarations.

·         Function definitions.  Define inline functions before calling them.  Defining static functions first reduces the need for prototypes.

It is OK to group data declarations and their related #defines and typedefs together if it makes the declarations easier to understand.

 

3.3     Header Files

Each .C file should have a companion .H (header) file.  A .H file has the same name as its companion .C file.  The .H file declares the data and functions in your .C file that other .C files may need.  The .H file does not contain information that only your .C file needs.  Put that information in your .C file.

 

There may also be one or more .H files that do not belong with a particular .C file.  Such .H files typically contain definitions for TRUE, FALSE, and similar "global" items.

 

Compiling a .H file must not produce code or allocate space for data.  Prototypes, typedefs, and macros, are OK.  Data declarations and inline functions must be extern.

 

3.3.1     Including Header Files

It must be possible to compile .H files independently of any .C file for syntax checking.  This means that each .H file must include any other .H files on which it depends.

 

Each .C file must explicitly include the .H files it needs.  It should not rely on one .H to include another.  Neither .C files nor .H files should include .H files they don’t need.  However, a .C file must include its own .H file, even if it does not need that .H file to compile correctly.  This ensures that there are no conflicts between the .C file and its .H file.

 

Each .H file must have a conditional to prevent compiling it more than once.  It must also have conditionals to prevent including other .H files more than once.  Include files in alphabetical order to make it easier to see whether a particular .H is included.  For example, MYFILE.H might look like:

#ifndef __MYFILE__

#define __MYFILE__

    .

#ifndef __FILE_A__

#include <file_a.h>

#endif

#ifndef __FILE_Z__

#include <file_z.h>

#endif

    .

#endif /* __MYFILE__ */

 

In .C files, include STDIO.H and other compiler-provided headers, without conditionals.  If there is a "universal" header file that nearly every .C file includes, that goes next, without a conditional.  The .H with the same name as the .C goes next, also without a conditional.  Include the remaining files in alphabetical order and use conditionals as for .H files.  For example, MYFILE.C might look like:

#include <stdio.h>

#include <igtdefs.h>

#include <myfile.h>

#ifndef __FILE_A__

#include <file_a.h>

#endif

#ifndef __FILE_Z__

#include <file_z.h>

#endif

 

3.3.2     Header File Organization

Arrange the contents of .H files in this order:

·         Version control header.

·         Conditional to prevent compiling the same .H twice.

·         File identification and modification history.

·         #include commands, with conditionals.

·         #define commands.

·         typedef declarations.

·         extern data declarations.

·         Function prototypes.

·         #endif for the conditional to prevent compiling the same .H twice.

It is OK to group data declarations and their related #defines and typedefs together if it makes the declarations easier to understand.

 

3.3.3     Text Header Files

A .C file may include a companion text header, or .TH, file.  A .TH file has the same name as its companion .C file.  A .TH file has text strings, usually in more than one language.  A .TH file does not contain functions.  Only one .C file may include a given .TH file.  .TH files do not use the conditional compiles that .H files use.

 

3.4     Naming Conventions

File names should indicate the purpose of the file as well as possible in eight characters.  For example, COMM_MGR.C is a communications manager.

 

Names in C often comprise two or more words or word fragments.  Use an underscore between the words or word fragments.  For example:

#define ELAPSED_MS(x) (timer_milliseconds() - (x))

typedef int wavelet_signal;

Boolean init_time(void);

 

Use all lower case letters for function names and variable names, including structure members.  Capitalize the first letter of each word or word fragment in a typedef.  Use all upper case for the names of macros and symbolic constants.

 

3.5     Self-Documenting Code

The C language lends itself to writing self-documenting code.  Self-documenting code means much more than simply using descriptive names, and it often produces better code.  Comments can easily be wrong; self-documenting code documents itself correctly.  The paragraphs below describe methods for producing self-documenting code.

 

Use descriptive names for constants, functions, typedefs, variables, etc.  Obviously, names like get_credits() and coins_bet are easier to understand than func2() and x.  Be consistent in your use of abbreviations.  For example, if you abbreviate manager as mgr in queue_mgr, use mgr as the abbreviation for manager throughout the product.  Naming functions with a verb and object, such as init_queue(), usually makes sense.  Try to keep the lengths of names within reasonable limits.

 

Use #define to define symbolic constants rather than using hard-coded values.  Changing the definition of the constant automatically changes the value in all code that references it.  Referencing the constant by name makes the code easier to understand.  Use #define to define macros that perform simple functions inline.  Such macros are especially useful when they perform tricky or error-prone functions.  For example:

#define QUART1_BASE 0x28010000

#define QUART1(reg) (*(volatile char*)(QUART1_BASE + (reg)))

#define CRA 0x04 /* Command Register a (CRa) */

QUART1(CRA) = 0;

conveys more information than

*(volatile unsigned char *)0x28010004 = 0;

Do not call macros with arguments that are assignments because the macro may evaluate them more than once.  Use parentheses in macro definitions to prevent any ambiguity when referencing the macro.  QUART1, above, has parentheses around the argument, reg, in case a caller passes a complex expression for an argument.  The parentheses around the entire definition for QUART1 ensure that we completely evaluate QUART1 before using the result in an expression.

 

Use an enumeration to group two or more related symbolic constants.  The enumeration should be either part of a variable definition or a typedef.  For example:

typedef enum

{

    FALSE = 0,

    TRUE = 1,

    OK = 0,             /* Return value for no errors */

    ERROR = 1           /* Return value for errors */

} Boolean;              /* Boolean type */

static enum

{

    STATE_IDLE = 0x1234,

    STATE_BUSY

} my_state;

Boolean my_function (Boolean flag)

{

    return flag && my_state == STATE_BUSY ? TRUE : FALSE;

}

is better than

static unsigned short my_state;

unsigned char my_function (unsigned char flag)

{

    return flag && my_state == 0x1234 ? 1 : 0;

}

 

Allow the compiler to assign the values in an enumeration unless you have a specific need to assign them yourself.  Many compilers allocate enumerations as the smallest integer type that can hold the specified values.  For example, the my_state enumeration, above, would be an unsigned short.  Take advantage of this feature if you are using a compiler that supports it.  Try to use enumerations, rather than other integer types, whenever applicable.  This applies to ordinary variables, function parameters, return values, and structure members.

 

Use a typedef whenever there are (or may be) multiple instances of a particular enumeration or structure.  Use a typedef in other situations where it will add clarity.  Using the same typedef in several declarations documents the fact that those declarations refer to the same kind of data or function.

 

Casts inform the compiler (and the reader) that you are treating one type as if it were another.  This is both useful and dangerous.  Try to write code that requires as few casts as possible.  Use casts when necessary to prevent compiler errors and warnings, but be sure the casts do not mask potential logic errors.

 

Choose the types for integer variables carefully.  When applicable, use an enumeration.  Use a long when the expected values exceed sixteen bits.  Use a char or short when the value will fit within 8 or 16 bits, respectively.  While many compilers use 32-bit ints, the C language only requires an int to be at least as big as a short.  Do NOT use an int to store values that may exceed 16 bits.  An int is usually the computer's word or register size.  Using ints for local variables will usually generate code that is more efficient.  Using shorts and chars saves memory, especially in arrays.  Always explicitly declare integer variables as unsigned unless a variable may contain a meaningful negative value.  This use of unsigned documents the fact that negative values are meaningless and eliminates the need to check for negative values when range-checking.

 

Limit the scope of data and functions whenever possible.  Try to avoid using global data.  If only one function references certain data, make that data local to the function.  If all references to a given function or data item are in the same source file, define the function or data as static.  In either case, do not declare local functions or data in a header file.  You can limit the scope of data even further by defining it within a block in a function.  This practice, while not common at IGT, has its place.  Define data within a block if it seems to help understanding the code.

 

Use const to designate constant data as such. Define local constant data inside a function as both static and const.  This allows compilers to allocate the data in read-only memory, saving RAM space.  Declare pointers to constant data as const *.  This is especially useful in function prototypes, because it documents the fact that the function must not alter the data.  Using const allows the compiler to give warnings if there is an attempt to write to constant data.

 

Use volatile to designate I/O ports and data that may change in non-obvious ways, such as due to interrupts.  Using volatile has no bearing on sequence points.

 

Declare inline functions as extern unless a function is inline in one .C file and public to other .C files.

 

Add parentheses to complex calculations when doing so will make them easier to understand.  Add braces where doing so will make the code easier to follow.

 

Use character constants, such as 'A', rather than hard-coded numbers, such as 0x45, to represent printable characters.

 

Every switch statement should have a default label.  This documents what the code does with unexpected values for the switch expression.

 

3.6     Structured Programming

The C language lends itself to structured programming.  Only a few constructs constitute unstructured programming.

 

The break (except in a switch statement), return (except as the last statement of a function), continue, and goto statements all violate the principles of structured programming.  This standard discourages, but does not prohibit, using these four statements.  Try to use them only when they clearly simplify the code or when the alternatives are contrived, harder to understand, or too inefficient.

 

Structured programming requires a break statement between case labels in a switch statement.  However, placing two or more case labels together, as if they were one case, is OK.  Allowing one case to "fall through" to another case saves code space, but violates the rules of structured programming.  However, the "fall through" technique makes it clearer that the two cases perform common functions, since they execute the exact same lines of code.  Use this technique carefully:

switch (state)

{

case IDLE:

default:

break;

 

case ADD:

inc_level(my_level, TOTAL);

state = UPDATE; /* Fall through to UPDATE */

case UPDATE:

update_level(my_level);

state = IDLE;

break;

}

 

3.7     Functions

Each function should have a single, clear purpose.  Functions should be as simple and straightforward as possible.  Deeply nested control structures suggest excessive complexity.  Try to limit functions to about 100 lines.

 

Declare functions using the ISO/ANSI standard.  This explicitly declares the types of the parameters and return value.  Never call a function without a prototype unless the call follows the function definition in the same .C file.

 

Whenever possible, pass data to functions through parameters, not global data.  However, if you must pass many data to a function, create a structure and pass a pointer to that structure.

 

Functions should use all of their parameters.  The only exception is when there is a need for a function pointer that may point to one of several functions.  In this case, pass all parameters to all such functions, even if some functions don't use all the parameters.

 

A function returning a success or failure indication should return zero for success and non-zero values for errors and failures.  If there is only one failure condition, the function should return OK (defined as 0) or ERROR (defined as 1).  A function whose name implies specific Boolean results should return the obvious values.  For example, is_the_door_open() should return TRUE (defined as 1) if the door is open and FALSE (defined as 0) if it is closed.  A function returning a pointer should return NULL (defined as ((void *)0)) if it fails.

 

Callers should check the return values of the functions they call.  This is mandatory if the failure of the called function might cause the caller to corrupt memory, etc.

 

 

4.     Comments

If we wrote code once and never looked at it again, there would be no need for comments.  However, we spend far more time debugging, reviewing, maintaining, and enhancing code than we spend writing it in the first place.  Good comments are essential to prevent the need to reverse-engineer the code every time someone looks at it.  Writing good comments also forces us to think through how the code works.  This sometimes reveals that the code isn't straightforward or has errors.  Writing good comments is just as important and just as difficult as writing good code.

 

Incorrect comments are worse than no comments at all.  Ensure that the comments you write are correct and unambiguous.  If you change or add code, correct the comments to reflect your changes.

 

Write clear, concise comments.  Ask yourself what a programmer would need to know in order to understand your code.  Do not use cryptic abbreviations in comments; spell out the words.  Capitalize acronyms, like EEPROM.  Line-by-line comments may be sentence fragments; other comments should be complete English sentences with correct spelling and punctuation.  Choose your words carefully to avoid confusion.  For example, do not confuse pointer with index.

 

Every comment should convey information that another programmer may find useful.  Do not include comments that only explain how the C language works.  Do not include comments that simply state what is obvious from looking at the code:

get_signal_length();        /* Get signal length(What a useless comment!) */

return OK;                /* Return OK (Another useless comment!) */

 

4.1     Identification and Modification History

Begin all C source files with an identification and modification history like this:

/*************************************************************************

    FILE:           FILENAME.C

 

    DESCRIPTION:    A brief description of the file's contents.

                    Typically a sentence or two.

 

    AUTHOR          DATE        MODIFICATION

    ------          ----        ------------

    Mukesh Motwani  dd mmm yy   New.

    Naresh Dusane   dd mmm yy   Description of changes.

*************************************************************************/

The AUTHOR, DATE, and MODIFICATION entries start in columns 5, 21, and 33, respectively.  The most recent modification is last.

 

4.2     Function Headers

Every function must have a function header.  The function header must provide all the information a caller needs to use the function correctly.  The standard function headers look like this:

/*************************************************************************

FUNCTION:       my_function

 

PURPOSE:        A few words to several paragraphs describing what the function does, how it does it, and why.

 

GLOBAL DATA:    List all data the function accesses, excluding parameters and local variables.

 

PARAMETERS:

String      Describe the parameter and what it means.  Do not repeat the type of the parameter.  That is obvious from the prototype.

 

RETURNS:        State what the return value means.  Is it the number of bytes in string, etc.?

*************************************************************************/

unsigned int my_function (const char *string)

    .

The headings all begin in column 1.  Indent the parameter names to column 5.  Indent everything else to column 17.  If a function accesses no global data, has no parameters, or returns void, enter None, None, or void, respectively.

 

4.3     Section Headings

It may be helpful, especially for longer source files, to add comments that introduce major sections of the file.  These might include #include statements, #define statements, enumerations, structure declarations, local data definitions, public data definitions, inline functions, local functions, and public functions.  For example:

/*************************************************************************

LOCAL DATA DEFINITIONS

*************************************************************************/

If you divide files into major sections like this, be sure everything is in the correct section.

 

4.4     General Documentation

Many C files, especially those providing services that may be called from many other files, should have a comment block that explains the overall objectives and design of the code.  Explain not only how to use each function, but how the functions interact.  Enclose such comments between lines of asterisks as for the section headings, above.

 

4.5     Documenting Data

Document every variable (except minor local variables).  This is especially important for parameters, return values, structure members, and other data that many functions may access.  Document the following items that apply:

·         Meaning: What, precisely, does the variable represent?  What do the various legal values mean?  How does the code use this variable?

·         Legal and illegal values: If it isn't obvious from the context and variable type, state what values are valid.

·         Units: Does this variable represent milliseconds, dollars, bytes, or some other unit of measure?

Careful design and the use of self-documenting techniques can greatly reduce the burden of documenting individual variables.

 

4.6     Comment Format

Except as described or shown above, do not include lines of asterisks, dashes, or other characters to set comments apart from code.  A single blank line before and after the comment is sufficient.  Do not draw boxes around comments with asterisks or other characters.  This makes it difficult to edit multi-line comments.

 

Here are some general examples and guidelines on comment format:

/*  This comment block explains the code that follows.  Indent the "/*" the same as that code.  Intent the text of the comment four additional spaces.  */

a = 1;                  /* Short comments follow the code they document */

set_interrupt_level(DISABLE_INTERRUPTS);

/* Comment goes on the next line if there isn't room */

 

You may right-justify line-by-line comments, as above.  You may select a column and indent all comments to start in that column.  Whichever you do, organize your comments consistently and neatly.

 

4.7     Comments in .C versus .H Files

Comments that describe the overall use of the facilities of a .C file belong in the .H file.  Comments describing internal details that a caller doesn't need to know belong in the .C file.

 

Document enumerations, structures, and other types where you declare them.  Additional comments may be necessary where you define an instance of a previously declared type.

 

Always document functions, including inline functions, in the file where you define them.  For functions that many .C files may call, you should also document them in the .H file, along with their prototypes.

 

4.8     Summary

Use line-by-line comments where needed.  If a few line-by-line comments aren't adequate, write full paragraphs of English language comments.  A short paragraph before a structure definition or a critical section of code can save others many hours of reverse engineering.  As stated before, ask yourself what a programmer would need to know in order to understand your code, and then provide that information.