CSc 352: C Coding Guidelines

The coding guidelines we will follow are based on GNU coding styles, with some modifications based on personal taste.  Your programs will be expected to follow these guidelines unless explicitly directed otherwise.
 

1. Robustness

2. Error Messages

3. Comments

  • Every program should start with a comment saying briefly what it is for. Example: `fmt - filter for simple filling of text'.
  •  Put a comment before each function saying what the function does, what sorts of arguments it gets, and what the possible values of arguments mean and are used for. It is not necessary to duplicate in words the meaning of the C argument declarations, if a C type is being used in its customary fashion. If there is anything nonstandard about its use (such as an argument of type char * which is really the address of the second character of a string, not the first), or any possible values that would not work the way one would expect (such as, that strings containing newlines are not guaranteed to work), be sure to say so.
  • Explain the significance of the return value, if there is one.
  • Write in complete sentences, in text that is (as far as possible) grammatically correct, and capitalize the first word (for example, avoid stuff like this). If a lower-case identifier comes at the beginning of a sentence, don't capitalize it! Changing the spelling makes it a different identifier. If you don't like starting a sentence with a lower case letter, write the sentence differently (e.g., "The identifier lower-case is ...").
  • Avoid references to classroom discussions or meetings without adequate mention of specific technical aspects of the discussion. Comments are intended for other people who may read your code, perhaps long after it was written: it may not be immediately obvious to them what was discussed in class. Instead, state explicitly what it is you're referring to. (It's OK to add references to classroom discussions or meetings as long as such references supplement, rather than replace, the technical information you're providing.)

    Thus, do not write something like

    The data structure used in this program is as discussed in class.

    Much better would be:

    The data structure used in this program is a hash table, where each hash bucket is organized as a balanced binary tree (as discussed in class).

  • 4. Clean Use of C Constructs

  • Explicitly declare the types of all objects. For example, you should explicitly declare all arguments to functions, and you should declare functions to return int rather than omitting the int and relying on the default.
  •  Declarations of external functions and functions to appear later in the source file should all go in one place near the beginning of the file (somewhere before the first function definition in the file), or else should go in a header file. Don't put extern declarations inside functions.
  • Header files should not contain function definitions: such definitions should be in *.c files.
  •  Try to avoid assignments inside if-conditions. For example, don't write this:
    if ((foo = (char *) malloc (sizeof *foo)) == 0)
      fatal ("virtual memory exhausted");
    instead, write this:
    foo = (char *) malloc (sizeof *foo);
    if (foo == 0)
      fatal ("virtual memory exhausted");
  • 5. Naming Files, Variables and Functions

    6. Information Hiding

    When you declare a struct or union, you should also define macros to access its fields.  Avoid accessing the fields directly in the main body of the program: instead, use these macros for accessing the fields.  Thus, avoid writing code like this:
    struct tn {
      int ntype;
      union {
        int nval;
        char *name;
        struct tn *child[2];
      } flds;
    } *tptr;

    ...

    if (tptr->ntype == EXPR_PLUS) {
      emit_code(tptr->flds.tn[0]);
      emit_code(tptr->flds.tn[1]);
    }

    Instead, write
    struct tn {
      int ntype;
      union {
          int nval;
          char *name;
          struct tn *child[2];
      } flds;
    } *tptr;

    #define Type(x)  ((x)->ntype)
    #define Value(x) (((x)->flds).nval)
    #define Name(x)  (((x)->flds).name)
    #define Child(x,i) (((x)->flds).child[i])

     ...

    if (Type(tptr) == EXPR_PLUS) {
      emit_code(Child(tptr,0));
      emit_code(Child(tptr,1));
    }

    The latter style is easier to understand (e.g., it's easier to see that Child(tptr,0) refers to the 0th child of the node that tptr points to than tptr->flds.tn[0]); and changes in the data structure, e.g., to improve performance, are easier to accommodate and less prone to bugs.


    Acknowledgements

    The material in this document is based on GNU coding styles.