Project Dependent Standards

Individual projects may wish to establish additional standards beyond those given here. The following issues are some of those that should be adddressed by each project program administration group.

  • What additional naming conventions should be followed? In particular, systematic prefix conventions for functional grouping of global data and also for structure or union member names can be useful.
  • What kind of include file organization is appropriate for the project’s particular data hierarchy?
  • What procedures should be established for reviewing lint complaints? A tolerance level needs to be established in concert with the lint options to prevent unimportant complaints from hiding complaints about real bugs or inconsistencies.
  • If a project establishes its own archive libraries, it should plan on supplying a lint library file [2] to the system administrators. This will allow lint to check for compatible use of library functions.

Conclusion

A set of standards has been presented for C programming style. One of the most important points is the proper use of white space and comments so that the structure of the program is evident from the layout of the code. Another good idea to keep in mind when writing code is that it is likely that you or someone else will be asked to modify it or make it run on a different machine sometime in the future.

As with any standard, it must be followed if it is to be useful. The Indian Hill version of lint will enforce those standards that are amenable to automatic checking. If you have trouble following any of these standards don’t just ignore them. Programmers at Indian Hill should bring their problems to the Software Development System Group (Lee Kirchhoff, contact) in department 5522. Programmers outside Indian Hill should contact the Processor Application Group (Layne Cannon, contact) in department 5512 (footnote 53).

Footnotes

  1. In fact, they’re pretty good general standards. This document is presented unadulterated; U of T variations, comments, exceptions, etc. are presented in footnotes.
  2. Of necessity, these standards cannot cover all situations. Experience and informed judgement count for much. Inexperienced programmers who encounter unusual situations should consult 1) code written by experienced C programmers following these rules, or 2) experienced C programmers.
  3. This is not a problem at U of T, or most other sensible places, but rows of asterisks are still annoying.
  4. Excessively long lines which result from deep indenting are often a symptom of poorly-organized code.
  5. A common variation, in both Bell code and ours, is to reverse the order of sections 1 and 2. This is an acceptable practice.
  6. Such defines should be indented to put the defines one level deeper than the first keyword of the declaration to which they apply.
  7. They should be in some sort of meaningful order. Top-down is generally better than bottom-up, and a breadth-first” approach (functions on a similar level of abstraction together) is preferred over depth-first (functions defined as soon as possible after their calls). Considerable judgement is called for here. If defining large numbers of essentially-independent utility functions, consider alphabetical order.
  8. In addition to the suffix conventions given here, it is conventional to use Makefile' (not makefile’) for the control file for make and README' for a summary of the contents of a directory or directory tree.
  9. Preferred. An alternate convention that may be preferable in multi-language environments is to use the same suffix as an ordinary source file but with two periods instead of one (e.g. foo..c'').
  10. No idea what this is.
  11. Don't use absolute pathnames for header files. Use the <name> construction for getting them from a standard place, or define them relative to the current directory. The -I option of the C compiler is the best way to handle extensive private libraries of header files; it permits reorganizing the directory structure without having to alter source files.
  12. It should be noted that declaring variables in a header file is often a poor idea. Frequently it is a symptom of poor partitioning of code between files.
  13. So should the constant names and their defined values.
  14. These defines are better put right after the declaration of type, within the struct declaration, with enough tabs after # to indent define one level more than the structure member declarations.
  15. The empty initializer, {}'', should never be used. Structure initializations should be fully parenthesized with braces. Constants used to initialize longs should be explicitly long.
  16. In any file which is part of a larger whole rather than a self-contained program, maximum use should be made of the static keyword to make functions and variables local to single files. Variables in particular should be accessible from other files only when there is a clear need that cannot be filled in another way. Such usages should be commented to make it clear that another file's variables are being used; the comment should name the other file.
  17. Some automated program-analysis packages use a different character in this position as a marker for lines with specific items of information. In particular, a line with a -‘ here in a comment preceding a function is sometimes assumed to be a one-line summary of the function’s purpose.
  18. A common practice in both Bell and local code is to use a space rather than a tab after the *. This is acceptable.
  19. Discussion of non-trivial design decisions is also appropriate, but avoid duplicating information that is present in (and clear from) the code. It’s too easy for such redundant information to get out of date.
  20. Neither Bell nor local code has ever included these separating blank lines, and it is not clear that they add anything useful. Leave them out.
  21. These rules tend to produce a lot of clutter. Both Bell and local practice frequently omits extern declarations for static variables and functions. This is permitted. Omission of declarations for standard library routines is also permissible, although if they are declared it is better to declare them within the functions that use them rather than globally.
  22. In fact, avoid any local declarations that override declarations at higher levels.
  23. This break is, strictly speaking, unnecessary, but it is required nonetheless because it prevents a fall-through error if another case is added later after the last one.
  24. Some judgement is called for in the case of complex expressions, which may be clearer if the inner” operators are not surrounded by spaces and the outer” ones are.
  25. Sizeof is an exception, see the discussion of function calls. Less logically, so is return.
  26. Trailing underscores should be avoided too.
  27. This convention is reserved for system purposes. If you must have your own private identifiers, begin them with a capital letter identifying the package to which they belong.
  28. It is best to avoid names that differ only in case, like foo and FOO. The potential for confusion is considerable.
  29. This difference also means that carefree use of macros requires care when they are defined. Remember that complex expressions can be used as parameters, and operator-precedence problems can arise unless all occurrences of parameters in the definition have parentheses around them. There is little that can be done about the problems caused by side effects in parameters except to avoid side effects in expressions (a good idea anyway).
  30. At the very least, any directly-coded numerical constant must have a comment explaining the derivation of the value.
  31. If you #ifdef dependencies, make sure that if no machine is specified, the result is a syntax error, not a default machine!
  32. The 3B is a Bell Labs machine. The VAX, not shown in the table, is similar to the 3B in these respects. The 68000 resembles either the pdp11 or the 3B, depending on the particular compiler.
  33. Any unsigned type other than plain unsigned int should be typedefed, as such types are highly compiler-dependent. This is also true of long and short types other than long int and short int. Large programs should have a central header file which supplies typedefs for commonly-used width-sensitive types, to make it easier to change them and to aid in finding width-sensitive code.
  34. Beware of making assumptions about the size of pointers. They are not always the same size as int. Nor are all pointers always the same size, or freely interconvertible. Pointer-to-character is a particular trouble spot on machines which do not address to the byte.
  35. The or operator ( | ) does not have these problems, nor do bitfields (which, unfortunately, are not very portable due to defective compilers).
  36. Actually, this is not quite the real reason why getchar returns int, but the comment is valid: code which assumes either that characters are signed or that they are unsigned is unportable. It is best to completely avoid using char to hold numbers. Manipulation of characters as if they were numbers is also often unportable.
  37. Actually, there are some more right-to-left machines now, but the comments still apply.
  38. The same applies to variables in general. Alignment considerations and loader peculiarities make it very rash to assume that two consecutively-declared variables are together in memory, or that a variable of one type is aligned appropriately to be used as another type.
  39. A particularly notorious case is using strcmp to test for string equality, where the result should never ever be defaulted. The preferred approach is to define a macro STREQ:
  40. An exception is commonly made for predicates, which are functions which meet the following restrictions:
    • Has no other purpose than to return true or false.
    • Returns 0 for false, 1 for true, nothing else.
    • Is named so that the meaning of (say) a `true’ return is absolutely obvious.

    Call a predicate isvalid or valid, not checkvalid.

  41. Actually, YES and NO often read better.
  42. But not too familiar. The internal details of library facilities, as opposed to their external interfaces, are subject to change without warning. They are also often quite unportable.
  43. Or, especially, writing your own code to control terminals. Use the termcap package.
  44. It also makes your code less readable, because the reader has to figure out whether you’re doing something special in that reimplemented stuff to justify its existence. Furthermore, it’s a fruitful source of bugs.
  45. The use of lint on all programs is strongly recommended. It is difficult to eliminate complaints about functions whose return value is not used (in the current version of C, at least), but most other messages from lint really do indicate something wrong. The -h, -p, -a, -x, and -c options are worth learning. All of them will complain about some legitimate things, but they will also pick up many botches.
  46. Yes.
  47. Little of this is relevant at U of T. The version of lint that we have lacks these mods.
  48. The ++ and operators count as assignment statements. So, for many purposes, do functions with side effects.
  49. Note also that side effects within expressions can result in code whose semantics are compiler-dependent, since C’s order of evaluation is explicitly undefined in most places. Compilers do differ.
  50. The continue statement is almost as bad. Break is less troublesome.
  51. The need to do such a thing may indicate that the inner constructs should be broken out into a separate function, with a success/failure return code.
  52. In other words, some of the visual layout is dictated by intent rather than syntax. Beautifiers cannot read minds.
  53. At U of T Zoology, it’s Henry Spencer in 336B.

References

  1. B.A. Tague, “C Language Portability”, Sept 22, 1977. This document issued by department 8234 contains three memos by R.C. Haight, A.L. Glasser, and T.L. Lyon dealing with style and portability.
  2. S.C. Johnson, “Lint, a C Program Checker”, Technical Memorandum, 77-1273-14, September 16, 1977.
  3. R.W. Mitze, “The 3B/PDP-11 Swabbing Problem”, Memorandum for File, 1273-770907.01MF, September 14, 1977.
  4. R.A. Elliott and D.C. Pfeffer, “3B Processor Common Diagnostic Standards- Version 1”, Memorandum for File, 5514-780330.01MF, March 30, 1978.
  5. R.W. Mitze, “An Overview of C Compilation of UNIX User Processes on the 3B”, Memorandum for File, 5521-780329.02MF, March 29, 1978.
  6. B.W. Kernighan and D.M. Ritchie, The C Programming Language, Prentice-Hall 1978.

Share
Tweet
Share
Pin