[comp.lang.c] Cottam's C Style Guide

ian@mucs.UX.CS.MAN.AC.UK (Ian Cottam) (03/03/88)

(This is a small revision to the list I posted recently.  I would like to
 thank the following for their constructive comments: P.Crowther,
 R.Tobin, D.Jones, R.Calbridge, T.Stockfisch, C.Forsyth, A.Jonasson,
 B.Todd, H.Spencer, M.Sullivan, J.Dzikiewicz, and B.Needham)

A few meta-comments are in order judging from the criticisms I have
received.
 1) This is a tiny part of my "C course notes".  Other sections discuss
    generally applicable programming habits and "C pitfalls" etc.  This
    list will always be terse and reflect my bias towards demonstratively
    correct (w.r.t formal spec) programs irrespective of the implementation
    language.  (Not something that is as popular in the USA as Europe :-) )
    It is (largely) C specific -- I have even removed the "don't assume
    ASCII" because that it is applicable to all languages.

 2) Several people said the points about asymmetric layout of the assignment
    operator versus symmetric layout of equality were interesting, but they
    had never seen it used and therefore they would not start now.  The
    same was said about my support of the comma operator to avoid side
    effects in expression context (see also point (1) above).  Because I
    program *into* several different languages every day (Aside: I have
    been using C on and off for 10 years for those people that wondered
    if I was new to it! :-) ) I know that, without a discipline, I am
    quite likely, for example, to use = to mean equality.
    I dislike the use of the conventional equality sign for assignment,
    but then it is only sugar upon the abstract syntax.
    Since adopting the conventions below I have never made the =/== slip.
    Arguments such as "I have never seen this done before" or "Dennis
    doesn't use it" have never carried much weight with me [:-)].

    The view that tools such as modified-lints or the recently posted check
    program should be used instead is a valid alternative.
    However, I occasionally use non-UNIX systems without lint;
    I also prefer not to make mistakes in the first place.

 3) My deprecation of side effect operations together with my use of the
    comma operator is to:
	a) permit Hoare logic style reasoning (when required);
	b) to follow the advice, often posted but rarely followed, about
           the dangers of premature optimisation;
	   [Aside: I fully support the use of side effects for extreme
	    efficiency in critical loops as recently demonstrated in
	    the staggeringly impressive version of egrep that was posted.
	    Such examples are not so common!
            Test:  remove all occurences of the
	    word register from your favourite program and measure the
	    speed degradation.]
	c) make the code more readable to "casual C users";
	d) simply prevent blunders I know myself to be all to capable
	   of making [e.g. missing the extra brackets in the awful
	   idiom ((x= call()) != 0)].

I will not post the list again and consider the discussion closed.
Advice is there to be ignored!  Or, to quote Monty to his batman:
``When I require the services of a sex consultant -- I will ask you.''
(admission: this is a deliberate misquote in case children are watching :-) )

____________________here-it-is_____________________________________

		Cottam's Good C Style Guide
		===========================

Note:  Advice that I, and a few others, have given but has been universally
       ignored is indicated by a + rather than a * in the list below!


* split programs over several files for ``information hiding''

* choose a personal/project standard for layout and stick to it

* do not rely on defaults, e.g. extern/static variables being set to 0
  [this is for readability -- I am not concerned with non-C compilers]

* use void when you are writing a non-value returning function
  (not the default int)

* cast the return values of functions to void if you are certain that
  their result is not needed

* use lint (if you have it) as a matter of course
  (not just after 10 hours debugging)

* write portable C (Kernel level code, e.g. drivers, excepting.  Put
  all such code in a separate file.)

* use standard I/O (i.e. don't use UNIX specific I/O without good reason)

* don't use a macro if a function call is acceptably efficient

* avoid complex macros

* don't use complex conditional expressions especially ones returning
  non-arithmetic values

* use typedef to build up (very complex) declarations piecemeal

* use enumerations and #define for constant names
  (enum is preferable to #define, especially with ANSI C)

* always check the error status of a system call and C library functions
  if impossible to prove exception can not occur

* use header files, e.g. <stdio.h>, rather than assume, e.g., -1 == EOF

* use casts rather than rely on representational coincidences (see also
  comments on use of lint and portability)

* only have one copy of global declarations in a .h file
  (of course there may be many header files)

* do not embed absolute pathnames in program code (at least #define them)
  put the strings in a separate file for ease of change and to minimise
  recompilation

* use static for variables and functions that are local to a file

* use the ``UNIX standard'' for command line argument syntax and getopt()
  for parsing

* don't use the preprocessor to make C look like anything but C

+ show the difference between = and == by using asymmetric layout
  for assignment (e.g. ptr= NULL) and symmetric layout for equality
  (e.g. ptr == NULL).

+ use  expr == var  to emphasise and catch some = for == mistakes
  (e.g. x*2 == y)

+ avoid EXCESSIVE use of multiple exits from a context
  (but be realistic about this when using  switch  statements)

+ don't use assignment inside a complex expression
  (e.g. use (chptr= malloc(N), chptr != NULL)
        rather than
            ((chptr = malloc(N)) != NULL)
  [but note that multiple assignments in statement context is no problem
   e.g. x= y= z= 0;]

+ avoid #ifdefs for version/revision control purposes (use a proper version
  control system)

+ use side-effect operations in statement context only
  (exception: the comma operator)

+ use local blocks to indicate the extent of variables and comments, e.g.
	{ /* this does foo */
		int foovar;
		/* stuff for foo */
	}
	{ /* this does bar */
		...etc...
	}