check out this old post I found on Google Groups.
this kind of stuff makes me giddy. :-)
-=-=-=-
Message-ID: <bnews.mit-vax.149>
Newsgroups: net.sources
X-Path: utzoo!decvax!harpo!eagle!mit-vax!mp
From: mit-vax!mp
Date: Tue Jun 29 08:07:31 1982
Subject: C coding standards
X-Google-Info: Converted from the original B-News header
Posted: Sun Jun 27 18:24:20 1982
Received: Tue Jun 29 08:07:31 1982
In my message in net.misc about coding standards, I offered to
send out the Stanford Network Graphics project's C style sheet.
Since almost everyone who responded to my query requested a copy
of that document, I decided to post it to the net.
In fact, thanks to JBrookshire@USC-ECLB, I now have 3 different C style
standards to give you. Each is separated by a line of "*********"s.
Mark Plotnick, MIT
************************************************
INGRES CODING CONVENTIONS FOR C/UNIX PROGRAMMING
University of California, Berkeley
"The Ingres data base system encompasses about 75,000 lines of code
in the programming language `C' and runs on top of the Unix operating system.
Over the past six years, Ingres has evolved into a functionally complete and
usable prototype. Development required 25 to 30 programmer-years by a total
of 19 people, and the systems is now in use at over 125 sites around the world."
Allman, Eric.; and Stonebreaker, Michael "Observations on the
Evolution of a Software System." COMPUTER 15, 6 (June 1982),
27-32.
The following represents the current C coding conventions that have
evolved from and during the development of the Ingres system. This document
has been provided by Joe Kalash of Berkeley <INGVAX.kalash at Berkeley> in
response to a general enquiry distributed via Arpanet BBOARDS.
/*
** CODE_CNVNTNS.C -- A Program to Display the INGRES Coding Style
**
** This hunk of code does virtually nothing of use. Its main
** purpose is to demonstrate the "official" ingres coding style.
**
** This demonstrates comments. There should be a block comment
** at the beginning of every file and/or procedure to explain
** the function of the code. Important information to put here
** includes the parameters of the routines, any options that the
** user may specify, etc.
**
** The first line of the comment should be a one-line description
** of what's going on. The remainder should be more detailed.
** Blank lines should seperate major points in the comments. In
** general, ask yourself the question, "If I didn't know what this
** code was, what it was for, how it fit in, etc., and if I didn't
** even have the documentation for it, would these comments be
** enough for me?"
**
** Some general guidelines for the code:
**
** ***** GENERAL SYNTAX *****
**
** - Commas and semicolons should always be followed by a space.
** Binary operators should be surrounded on both sides by
** spaces. Unary operators should be in direct contact
** with the object that they act on, except for "sizeof",
** which should be seperated by one space.
**
** - Two statements should never go on the same line. This includes
** such things as an if and the associated conditionally
** executed statement.
** In cases such as this, the second half of the if
** should be indented one tab stop more than the if. For
** example, use:
** if (cond)
** statement;
** never:
** if (cond) statement;
** or:
** if (cond)
** statement;
**
** - Braces ({}) should (almost) always be on a line by them-
** selves. Exceptions are closing a do, and terminating
** a struct definition or variable initialization. Braces
** should start at the same indent as the statement with
** which they bind, and code inside the braces should be
** indented one stop further. For example, use:
** while (cond)
** {
** code
** }
** and never:
** while (cond)
** {
** code
** }
** or:
** while (cond) {
** code
** }
** or:
** while (cond)
** {
** code
** }
** or anything else in that line. Braces which match
** should always be at the same tab stop.
**
** - Do statements must always be of the form:
** do
** {
** code;
** } while (cond);
** even if "code" is only one line. This is done so that
** it is clear that the "while" is with a do, rather than
** a standalone "while" which is used for the side effects of
** evaluation of the condition.
**
** - There should always be a space following a keyword (i.e.,
** for, if, do, while, switch, and return), but never
** between a function and the paren preceeding its
** arguments. For example, use:
** if (i == 0)
** exit();
** never:
** if(i == 0)
** exit ();
**
** - Every case in a switch statement (including default) should
** be preceeded by a blank line. The actual word "case" or
** "default" should have the indent of the switch statement plus
** two spaces. It should be followed by a space (not a
** tab) and the case constant. Multiple case labels on
** a single block of code should be on seperate lines, but
** they should not be seperated by blank lines. The
** switch statement should in general be used in place of
** such constructs as:
if (i == 1)
** code1;
** else if (i == 34)
** code2;
** else if (i == -1643)
** code3;
** which can be more succinctly stated as:
** switch (i)
** {
** case 1:
** code1;
** break;
**
** case 34:
** code2;
** break;
**
** case -1643:
** code3;
** break;
** }
** In point of fact the equivalent switch will compile
** extremely efficiently. (Note that if you had some
** instance where you could not use a case, e.g., checking
** for i < 5, else check for j > 3, else whatever, then
** the above ("if") code is in the correct style. However,
** if (i < 5)
** code1;
** else
** if (j > 3)
** code2;
** else
** code3;
** is acceptable.
**
** - A blank line should seperate the declarations and the code
** in a procedure. Blank lines should also be used freely
** between major subsections of your code. The major
** subsections should also have a block comment giving
** some idea of what is about to occur.
**
** ***** PREPROCESSOR USAGE *****
**
** - Fields of #defines and #includes should line up. Use:
** # define ARPA 25
** # define MAXFIELDS 18
** and not:
** #define ARPA 25
** #define MAXFIELDS 18
** Conditional compilation (#ifdef/#endif) should be used
** around all trace information, timing code, and code
** which may vary from version to version of UNIX. See
** the code below for an example of conditional compila-
** tion use.
**
** ***** VARIABLES AND DECLARATIONS *****
**
** - Defined constants (defined with the # define feature) must
** be entirely upper case. The exceptions to this are
** compilation flags, which begin with a lower case "x",
** and some sub-types for parser symbols. In any case,
** the majority of the symbol is upper case.
**
** - Global variables should begin with an upper case letter and
** be otherwise all lower case. Local symbols should be
** entirely lower case. Procedure names are all lower
** case. The only exception to this is the trace routine
** "tTf". You should avoid user non-local symbols (globals
** or # define'd symbols) which are one character only;
** it is impossible to distinguish them. Capitalization
** may be used freely inside global names so long as they
** are primarily lower case; for example, "ProcName" is
** an acceptable name (and preferred over either Proc_name
** or Procname).
**
** - Use descriptive variable names, particularly for global var-
** iables. "IEH3462" tells me nothing; nor does "R". On
** the other hand, "Resultid" tells me quite a lot,
** including what it might be, where I might go to see
** how it is initialized, etc. Try not to use variables
** for multiple purposes. Variable names like "i" are
** acceptable for loop indices & temporary storage
** provided that the value does not have long-term
** semantic value.
**
** - When the storage structure or type of a variable is
** important, always state it explicitly. In particular,
** include "auto" if you are going to take the address
** of something using the ampersand operator (so that
** some wayward programmer doesn't change it to register),
** and declare int parameters as int instead of letting
** them default.
**
** ***** GENERAL COMMENTS *****
**
** - It is quite possible to name a file "printr.c" and then
** put the code for "destroydb" in it. Try to arrange
** the names of your files so that given the name of a
** routine, it is fairly easy to figure out which file
** it is in.
**
** - Sometimes it is really pretty much impossible to avoid doing
** something tricky. In these cases, put in a comment
** telling what you are doing and why you are doing it.
** - Try to write things that are clear and safe, rather than
** things which you think are easier to compile. For
** example, always declare temporary buffers as local,
** rather than as global. This way you can another
** routine accidently when it still had useful info
** in it.
**
** ***** COMMENTS *****
**
** - The importance of comments cannot be overemphasised.
** INGRES is primarily a research vehicle rather than
** a program product. This means that there will be
** many people pouring over your code, trying to
** understand what you have done & modify it to do
** other things. Try to make life easy for them &
** maybe they will be nice to you someday.
**
** - Try to keep an idea of the flow of your program. Put
** block comments at the beginning of major segments,
** and use one-line comments at less major junctures.
** A person viewing your code at ten paces should be
** able to tell where the major segments lay.
**
** - The preferred format for block comments is to begin with
** a line containing slash-star alone, followed by a
** number of lines all beginning star-star containing
** the comment, and terminating with a line containing
** star-slash alone. Comments without the double-star
** at the beginning of each line should be avoided,
** since it makes the comments seemingly disappear into
** the body of the code.
**
** - The beginning of each routine should have a comment block
** in parametric form as demonstrated below. The fields
** "Parameters", "Returns", and "Side Effects" should
** be considered mandatory. Mark parameters as being
** (IN), (IN/OUT), or (OUT) parameters, depending on
** whether the parameter is used only to transmit infor-
** mation into the routine, in and out of the routine,
** or only to return information; the default is (IN).
**
** Remember, it is easy to write totally incomprehensible code in
** C, but we don't go in for that sort of thing. It isn't too
** much harder to write brilliantly clear code, and the code is
** worth a lot more later.
**
** For efficiency reasons, you should always use register variables
** when possible. A simple and extremely effective tip is to define
** a register variable, and assign an oft-used parameter to it,
** since it is particularly inefficient to reference a parameter.
** Another particularly inefficient operation is referencing arrays
** of structures. When possible, define a register pointer to the
** structure, and then say:
** struct xyz structure[MAX];
** register struct xyz *p;
** ...
** for (i = 0; i < MAX; i++)
** {
** p = &structure[i];
** p->x = p->y + p->z;
** (diddle with p->???)
** }
** and not:
** struct xyz structure[MAX];
** ...
** for (i = 0; i < MAX; i++)
** {
** Structure[i].x = Structure[i].y + Structure[i].z;
** (diddle with Structure[i].???)
** }
** Remember, the nice things about register variables is that they
** make your code smaller and they run faster. It is hard to
** lose with registers. There are three restrictions which you
** should be aware of on register variables, however. First,
** The only types which may be registers are int's, char's,
** and pointers. Second, there may only be three register
** variables per subroutine. Third, you may not take the address
** of a register variable (i.e., you may not say "&i" if i is
** typed as a register variable).
**
** Usage:
** example [flags] argument
**
** Positional Parameters:
** argument -- this gets echoed to the standard
** output.
**
** Flags:
** -n -- don't put a newline at the end.
** -x -- don't do anything.
** -b -- echo it with a bell character.
**
** Return Codes:
** 0 -- successful
** else -- failure
**
** Defined Constants:
** XEQ1 -- maximum number of simultaneous equijoins.
**
** Compilation Flags:
** xTRACE -- enable trace information
**
** Trace Flags:
** 5 -- general debug
** 6 -- reserved for future use
** Compilation Instructions:
** cc -n example.c
** mv a.out example
** chmod 755 example
**
** Notes:
** These comments don't apply to the code at all,
** since this is just an example program.
** Also, it is wise to avoid this many comments
** except at the head of main programs and
** at the head of major modules. For example,
** this sort of commenting is appropriate at
** the top of ingres.c (system startup) and
** view.c (virtual view subsystem), but not
** in small utility routines, e.g., length.c.
** This sort of routine should be limited to
** "Parameters:", "Returns:", "Side Effects:",
** and anything else that seems relevant in
** that context.
** A fairly large comment block should exist at the
** top of modules [files] which contain many
** procedures; this block should clarify why
** the procedures are grouped together, that
** is, their common purpose in life. A small
** block should occur at the top of each
** procedure explaining details of that proce-
** dure.
** Procedures should be on seperate pages (use the
** form feed character, control-L).
** A program will help you generate this comment block.
** In ex, go to the line where you want to insert
** a block and say "so /mnt/ingres/comment". It
** will ask you for a block type, e.g., "main"
** for main program, "modfn" for a file which
** contains only one function, "function" or
** "procedure" for a procedure within a module,
** "module" for a module header (e.g., as a
** seperate comment block for a major module
** [check .../qrymod/view.c for an example] or
** in header files.
** SCCS should be considered an essential tool, if only
** to maintain history fields.
**
** Deficiencies:
** It should handle pseudo tty's.
*/
/* the following macro is defined by <sccs.h> */
SCCSID(%W%); /* %W% is replaced by a version number by SCCS */
# define XEQ1 5
struct magic
{
char *name; /* name of symbol */
int type; /* type of symbol, defined in symbol.h */
int value; /* optional value. This is actually
* the value if it is type "integer",
* a pointer to the value if it is a
* string. */
};
struct magic Stuff;
main(argc, argv)
int argc;
char *argv[];
{
register struct magic *r;
register int i;
register int j;
int timebuf[2];
auto int status;
/*
** Note that in the declarations of argc and argv above, all
** parameters to any function should be declared, even if they
** are of type int (which is the default).
*/
r = &Stuff;
/* initialize random # generator */
time(timebuf);
srand(timebuf[1]);
/* scan Stuff structure */
for (i = 0; i < XEQ1; i++)
{
# ifdef xTRACE
if (tTf(5, 13))
printf("switch on type %d\n", r->reltype);
# endif
switch (r->type)
{
case 0:
case 1:
case 3:
/* initialize */
printf("hi\n");
break;
case 2:
/* end of query */
printf("bye\n");
break;
default:
/*
** be sure to print plenty of info on an error;
** "syserr("bad reltype");" would not have been
** sufficient. However, don't make syserr's
** overly verbose; they take much space in the
** object module, and it will probably be
** necessary to look at the code anyway.
*/
syserr("main: bad type %d", r->type);
}
}
/* resist the temptation to say "} else {" */
if (i == 5)
{
i++;
j = 4;
}
else
i--;
/* plot the results */
do
{
i = rand() & 017;
plot(i);
} while (j--);
/* wait for child processes to complete */
wait(&status);
/* end of run, print termination message and exit */
for (i = 0; i < 2; i++)
printf("bye ");
printf("\n");
}
** PLOT -- Plot a Bar-Graph
**
** Does a simple plot on a terminal -- one line's worth.
**
** Parameters:
** n (IN) -- number of asterisks to plot
**
** Returns:
** none
**
** Side Effects:
** none
**
** Deficiencies:
** Should allow scaling.
*/
plot(n)
int n;
{
register int i;
for (i = n; i-- > 0; )
{
printf("*");
}
printf("\n");
}
**************************************************************************
BBN PROGRAMMING STANDARDS FOR C
The following document was provided by Dan Franklin <DAN at BBN=UNIX>
in response to a general enquiry distributed to Arpanet BBOARDS:
Here's a short set of guidelines that represents a combination
of the thoughts of two groups within BBN. I was its last editor.
Hope you find it useful. If you come across something more
comprehensive (which this certainly isn't), I'd appreciate it
if you let me know.
PROGRAMMING STANDARDS AND PRACTICES
FOR THE UNIX COST CENTER
This memo proposes a set of practices that might be followed in
the cost center, in order to have a consistently organized and
written, readable, understandable, and ultimately maintainable
software package. Qualifying each of the items below is an (S)
or (P): the former indicates that, once agreed-upon, the rule
must be adhered to rigorously and consistently--a Standard; the
latter items are more of the flavour of "good programming
Practice"--once agreed-upon, programmers should follow the
practice to the extent that common sense dictates.
1. DOCUMENTATION OF ROUTINE INTERFACES
(S) Every routine should be prefaced by a standard header
which describes the routine and its interface precisely, so that
the routine could be used by someone without reading the code.
The rationale for such a header is that it concentrates the critical document-
ation in one place, making it easy for someone (including the original
designer/coder) to use the routine correctly, and allows for the possibility
of automatically stripping off all headers to form an Internal Documentation
document.
In some cases a routine may do a small amount of processing and then
call another routine. (As an example, a routine may provide a
"special case" interface to another more general routine with
a more cumbersome calling sequence.) In this case, a routine's header
may refer the reader to the header of another routine for further information.
The format suggested is illustrated below: comments are surrounded by
< > symbols. Any item that is irrelevant, e.g., no parameters,
no return value, no error conditions, may be omitted.
/*------------------------ E P R I N T -----------------------------------*/
/* <a brief description of the routine...also, put the routine name in
* caps in the line above. The description should give, in general
* terms, the routine's function, including an overall description of its
* parameters, return values, and effect on global databases. It should also
* describe any error conditions that may occur and be reflected to the
* caller. The return value must be described precisely.>
*/
int eprint(estrucp, istart, ilength)
struct estruct *estructp; /* Pointer to eprint's structure */
int istart; /* Place in structure to start from */
int ilength; /* # things in struct to print */
{
}
Note that each parameter gets its own declaration line, and has a
comment to convey its exact meaning.
Each file in a program or subsystem consisting of multiple files
should start with some additional information, giving (essentially)
the justification for putting all these routines in one file.
That is, it should include a description of the common function
they all perform, the related functions they all perform, the database
they have in common and which is contained only in this file, etc.
In the last case, especially, a detailed description of the database
should be included; otherwise, since the database is not part of any
one routine, it will not get described in any other comment field.
The detailed description may require reading comments associated
with the actual declarations in order to be complete.
This header should include a history list describing all modifications
to functions in this file. Each entry in the list should give the
person, the date, the names of the functions (or database elements)
changed, and the reasons.
For the file containing the main routine of a program, the following
standards should be observed:
1. The main routine should be the first routine in the file.
2. Before the main routine should appear an overall comment field
describing the calling sequence in some detail. Generally, the
program will also be described in a manual page as well; this
comment field need not duplicate the entire contents of the
manual page, but it should include information about the overall
structure of the program which would be useful to a maintainer.
3. The main routine should exit by calling exit(0) explicitly.
All programs, however, must return an exit status of zero on success,
and nonzero on error.
2. FORMATTING
(S) All programs must be indented, at least 4 spaces per
indentation level. Statements at the same nesting level should
be at the same indentation level; statements at deeper nesting
levels should be indented more. (For the purposes of this
standard, the keyword "else" is regarded as having the same
nesting level as the "if" it corresponds to.) For example, the
following indentation practices are not permitted:
if (condition)
statement-group;
if (condition)
statement-group; else
statement-group;
if (condition)
for (init; test; step)
statement-group;
if (condition) statement-group;
else statement group;
(S) Nesting the trinary conditional operator (?:) is not permitted.
(P) For internal formatting and indenting style, the following is recommended:
. 4-space indentations at each level;
. braces delineating blocks used in the style of
if (exp)
{
code
}
or
if (exp)
{
code
}
. a separate declaration statement for each pointer,
due to the confusion that can arise when reading
and modifying a declaration like
char *a, b, *c;
. comments lined up systematically;
. spaces between reserved words and their opening parentheses,
e.g., if (condition) rather than if(condition);
. no deep nesting (greater than 4 levels deep)
. statement blocks less than 1 page long
. one statement per line
. parentheses around the objects of sizeof and return.
(P) Concerning internal comments:
. Full English sentences are recommended.
. It is often clearer and more readable to make
small blocks of comments as "paragraph headers" to a block of
code than to comment every line or so, especially if the code
is straightforward and untricky (which most of it should be,
especially if the programmer is conscientious about variable
naming and datatype compatibility).
. Anything non-obvious should be well-commented.
(P) Concerning white space and other aids to readability and debugging:
. blank lines should be used between blocks of code that are
functionally distinct
. in expressions, operators should be surrounded by blanks
(but not operators which compose primaries, specifically
".", "->")
. in a function definition, the function name and its datatype
should appear on one line. Examples:
int pread(...)
SOMESTRUCT * function(param1, param2)
This latter practice aids readability and the comparison of
the definition of a routine with its uses.
(P) Other suggestions for improving readability:
. Side effects within expressions should be used sparingly.
It is recommended that no more than one operator with a side
effect (=, op=, ++, --) appear within an expression. Function
calls with side effects count as operators for this purpose.
. In an "if-else" chain, the form
if (condition)
statement;
else if (condition)
statement;
else if (condition)
statement;
should only be used when the conditions are all of the same basic
form: e.g., testing the same variable against different values.
If the conditions are qualitatively different, the additional
"if" statements should start on new lines, indented, as in
if (cond)
statement;
else
if (cond2)
statement;
else
statement;
. In the condition portion of an if, for, while, etc., side effects
whose effect extends beyond the extent of the guarded statement
block should be minimized. That is, in a block like
if ((c = getc(file)) != EOF)
{
guarded-stmts
}
other-stmts
it is natural to think of "c" being "bound" to a value only
within
the guarded-stmts; use of a variable set or modified in a condition,
outside the range of the statements guarded by the condition, is
quite distracting.
. The use of || and && with right hand operands having side effects
is discouraged. Also, whenever || and && are mixed in the same
expression, parentheses should be used for clarity.
3. MODULARITY
3.1. Routine Length
(P) Routines should be kept reasonably short. In general, a routine
(or command) processes one or more inputs and generates one or
more outputs, where each of the inputs and outputs can be concisely
described. Usually a routine should only perform one function. Signs
that a routine is too long, and ought to be split up, are: length
greater than two pages; heavy use of "internal" variables (variables
whose scope is less than the entire routine).
3.2. Shared Data and Include Files
(S) Declarations of variables that are to be shared among several routines
in a package should be placed at the beginning of the file containing
the routines. If they are entirely internal to the package, they should
be declared "static" to hide them from other files.
(S) Declarations of structures and global variables used for communications
between a package and its callers should be placed in an appropriately
named .h file. Shared global variables should be declared extern in the
.h file so that if the package which sets/uses them is accidentally not
loaded, a diagnostic will be issued by the loader.
(S) Definition and optional initialization of such globals should reside in
one and only one file for each program. If a global is clearly associated
with a specific package, it may be defined in the file for that package;
otherwise, it should be placed in a file called data.c which is
compiled and linked with the other routines when a program is
being built.
(P) Use of globals should be minimized by judicious use of parameters.
Note that while static and extern designators in C both yield "common"
storage allocation, static allows the named variable to be known only
within the context in which it is declared; thus, an identifier declared
static at the top of a file will only be known within the file.
In addition, local symbolic constants (#defines) may occur anywhere within
a routine, and may be placed below the #include directives if the writer
feels that this aids readability.
(S) Include files should contain all and only all of the following items
necessary for a given program and shared among two or more of its files:
. #defines
. typedefs
. extern declarations
(S) Include files may also contain nested #include directives. However, this
practice should be kept to a minimum. It is recommended that
include files which may be embedded in this way contain a check
to see whether they are being included twice, to avoid unnecessary
preprocessor and compiler complaints when the user includes them
both implicitly and explicitly. The simplest way to do this is to
check for a special hidden #define, as in
#ifdef _INCLUDEFILENAME
#define _INCLUDEFILENAME
.
. Contents of include file
.
#endif _INCLUDEFILENAME
3.3 Routine interfaces
(P) In general, a routine should be designed with a "natural",
easily-remembered calling sequence. Routines with more than
five arguments are not recommended. Routines with "op-code"
arguments, where one argument determines the interpretation
and functions of the others, are also not recommended (though
they may prove useful as internal routines to a package,
they should NOT be part of a package's documented interface).
4. PORTABILITY
(P) Adherence to datatype compatibility should be practiced where
reasonable. This can be facilitated by liberal use of C's typedef
facility and explicit casting of types, as well as by the
use of the union datatype.
(P) The following violation of strict adherence is permitted: a package
which returns a pointer to a structure whose format need not be known
outside that package may return a "generic" pointer, of type (char *).
It is recommended that the typedef PTR be used for this purpose; this
typedef will be provided in some standard system file. Note that char*
is specifically chosen because the language guarantees that any pointer
may be converted to a char* and back again without harm.
(S) Liberal use of #defines should eliminate "magic numbers", whether
machine dependent or implementation dependent or arbitrary/random.
(S) To the extent that the conditional compilation facility of C allows, non-
portable constructions can use this facility. Failing this, machine or
implementation dependent constructs should be visibly commented in the
code.
When using conditional compilation for portability purposes, be sure
to use appropriate parameters, rather than machine type, wherever
possible. For example, code which handles the C machine's 20 bit wordsize
or 10 bit bytesize should use the parameter BYTESIZE in cpu.h rather
than being ifdeffed on "mbb".
(P) Up to six register variables can be declared (with some effect)
on the C70 machine, but only three on the 11/70 (where additional ones
are assigned regular automatic storage). Therefore, the first three
register variables declared (in lexicographic order) should be ones
for which the most gain can be gotten.
Note that register variables, judiciously chosen, can be very good space/time
savers, but the compiler is not overly smart about them, and can give you
irritating "expression overflow" messages. In general, as with any
"optimization", it is wise to design, code and debug first, and then add in
register storage to one's declarations.
5. NAMING
(P) Names should be chosen for their mnemonic nature. Recall that although
there is a limit on the number of initial characters of a name that must
be distinct in C (and for a given machine), this does not prevent any
name from being longer if such length will aid readability.
(S) It is a useful C convention to use upper-case for #defines and typedef
names.
(S) One exception to the above is for parameterized #defines.
These may be in lower-case.
**************************************************************************
The following was provided via FTP by Keith A. Lantz at Stanford
<CSL.LANTZ at SU-SCORE> via FTP following the enquiry distributed via
Arpanet BBOARDS.
1
NETWORK GRAPHICS C STYLE SHEET
Andrew Shore, et al.
Computer Systems Laboratory
Stanford University
Stanford, CA 94305
18 June 1982
1. Names
Don't use capital letters in file names (except for V !?).
Avoid the underscore.
Global variables, procedures, structs, unions, typedefs, defined constants,
and macros all begin with a capital letter, and are logically capitalized
thereafter (e.g. MainHashTable). A global variable is one defined outside a
procedure, even though it may not be exported from the file, or an external
variable. The motivation for treating macros and constants this way is that
they may then be freely changed to procedure calls or (global or external)
variables.
Local variables begin with a lower-case letter, but are logically capitalized
thereafter (e.g. bltWidth, power, maxSumOfSquares). Fields within structures
or unions are treated in this manner also.
2. Comments
There are generally two types of comments: block-style comments, and on-the-
line comments. Multi-line, block-style comments will follow the UNIX style of
/* and */ appearing on lines by themselves, and the body of the comment will
start with a properly aligned *. The comment should usually be surrounded by
blank lines as well. The motivation for this is that it is easy to add/delete
first and last lines, and it is easier to detect the common error of omitting
the */ and thus including all code up to and including the next */ in a comment
(Yapp helps with that too).
/*
* this is the first line of a multi-line comment,
* this is another line
* the last line of text
*/
On-line comments are used to detail declarations, and to explain single lines
of code. And, I suppose, for brief (i.e. one line) block-style descriptive
comments.
1
This work was supported by the Defense Advanced Research Projects Agency
under contract number MDA903-80-C-0102.
2
Procedures are preceded by block-style comments, explaining their (abstract)
function in terms of their parameters, results, and side effects. Note that
the parameter declarations are indented, not flushed left.
/*
* Unblock:
* unblock the process pd
*/
Unblock( pd )
register Process pd; /* the process to unblock */
{
register Process *currPd,
*tmpPd;
register unsigned prio;
prio = pd->priority;
Disable;
pd->state = Ready;
/* Add pd to the ready queue */
currPd = (Process *) &ReadyqHead;
while ((tmpPd = currPd->link) != Null)
{
if (tmpPd->priority > prio)
break;
currPd = tmpPd;
}
pd->link = tempPd;
currPd->link = pd;
Enable;
}
3. Indenting
The above example shows many of the indenting rules. Braces ( "{" and
"}" )
appear alone on a line, and are indented two spaces from the statement they are
to contain the body of, the body is indented two more spaces from the braces
(for a total of four spaces). else's and else if's line up with their
dominating if statement (to avoid marching off to the right, and to reflect the
semantics of the statement).
3
if ((x = y) == 0)
{
flag = 1;
printf (" the value was zero ");
}
else if (y == 1)
{
switch (today)
{
case Thursday:
flag = 2;
ThursdayAction();
break;
case Friday:
flag = 3;
FridayAction();
break;
default:
OtherDayAction();
}
}
else
printf(" y had the wrong value ");
4. File Contents
I think we agreed on the following order for file contents.
1. initial descriptive comment (see example below) which contains:
a. file name, with indication of the relative path to it when
relevant
b. brief descriptive abstract of contents
c. a list of all defined procedures in their defined order
d. list of authors
e. list of current maintainers
f. list of recent and major modifications in reverse
chronological order with indication (initials) of who made the
change.
2. included files (use relative path names whenever possible)
3. external definitions (imports and exports)
4. function declarations (externals and forward references)
4
5. constant declarations
6. macro definitions
7. type definitions
8. global variable declarations
9. procedure and function definitions
Here is an example of the header comment:
/*
* FILE: libc/vkstuff/malloc.c
*
* CONTENTS:
* C storage allocator stolen and hacked up from UNIX for the SUN
* (NOTE: these were stolen and have the wrong naming conventions)
* malloc
* free
* realloc
* allock DEBUG ONLY!
* SetBrk ! our routine to fake up sbrk()
*
* AUTHORS: stolen and hacked by Andrew I. Shore (shore)
*
* MAINTAINER: shore
*
* HISTORY:
* 03/05/82 AIS replaced calls to UNIX sbrk() with calls to own version
* SetBrk() for the sun/Vkernel
*/
5. Parenthesis ()
We seem to have decided on the following conventions for parentheses. When
parentheses enclose the expression for a statement (if, for, etc.), the
parentheses `belong to' the expression, so there is a space between the keyword
and the parenthesized expression. For function calls the parentheses `belong
to' the call, so there is no space between function name and open paren (there
may some inside the parentheses to make the expression list (argument list)
look nice).
if (Mumble())
{
Grumble( (a = b) == 0 );
return (Nil);
}
else
{
Fumble( a, b, c );
return (ToSender);
}
5
It is unclear what to do with return. Note, then, that it is operators that
cause spaces on the outside of parentheses -- (a + b) * c.
6. General
1. One statement/declaration per line.
2. Make judicious use of blank lines.
a. At least 3 blank lines between individual procedures.
b. Blank lines surround "large" comments.
3. Make sure comments and code agree!
4. Don't just echo the code with comments -- make every comment count!
i.e. nix on:
/* add foo to bar */
bar += foo;
i
Table of Contents
1. Names 1
2. Comments 1
3. Indenting 2
4. File Contents 3
5. Parenthesis () 4
6. General 5
**************************************************************************
-------
Date: 27 Jun 1982 11:48:50-PDT
From: mo at LBL-UNIX (Mike O'Dell [system])
To: JSol at USC-ECLC
cc: Header-People at MIT-MC
Subject: Re: Unexplored Topic -- Length of Mail Message
In-reply-to: Your message of 25 Jun 1982 1715-PDT (Friday).
Yes indeed mail can be BIG!! If you don't have FTP to some
random system (again, not ARPAnet, most likely), but DO have
mail, you do a LOT with it, like abuse it into make-do FTP.
It is crass, but it beats not getting the bits there!
This can often be the case with site 2 networks away in
the internet (note small "i").
While this shouldn't impact ARPAnet sites too much, if 733 is
indeed something the larger world might look to for guidance,
I would advocate JSol's position: Mail is another form of
machine-machine communications, complete unto itself.
As an example, the "net.sources" news topic on USENET
routinely posts fixes and entire source listings of quite
complex programs; and this is on a network with 300 or 1200 baud
hop-by-hop links!! I am not saying this is the ulitimate solution,
or that we should advocate such things, but it does indicate
the tremendous utility of Mail in reducing the isolation of
sites.
-Mike
Date: 27 Jun 1982 0929-PDT
From: JBROOKSHIRE at USC-ECLB
Subject: Re: C programming standards
To: mp at MIT-MC
In response to your message sent Saturday, 26 June 1982, 23:56-EDT
Thanks for the note - if you have any pointers it might help. I have received
three pretty good sets of stuff that I am enclosing here if you are interested.
They are separated by lines of ******** which you can search on if you just
want to read the sources of each.
Regards,
Jerry Brookshire
INGRES CODING CONVENTIONS FOR C/UNIX PROGRAMMING
University of California, Berkeley
"The Ingres data base system encompasses about 75,000 lines of code
in the programming language `C' and runs on top of the Unix operating system.
Over the past six years, Ingres has evolved into a functionally complete and
usable prototype. Development required 25 to 30 programmer-years by a total
of 19 people, and the systems is now in use at over 125 sites around the world."
Allman, Eric.; and Stonebreaker, Michael "Observations on the
Evolution of a Software System." COMPUTER 15, 6 (June 1982),
27-32.
The following represents the current C coding conventions that have
evolved from and during the development of the Ingres system. This document
has been provided by Joe Kalash of Berkeley <INGVAX.kalash at Berkeley> in
response to a general enquiry distributed via Arpanet BBOARDS.
/*
** CODE_CNVNTNS.C -- A Program to Display the INGRES Coding Style
**
** This hunk of code does virtually nothing of use. Its main
** purpose is to demonstrate the "official" ingres coding style.
**
** This demonstrates comments. There should be a block comment
** at the beginning of every file and/or procedure to explain
** the function of the code. Important information to put here
** includes the parameters of the routines, any options that the
** user may specify, etc.
**
** The first line of the comment should be a one-line description
** of what's going on. The remainder should be more detailed.
** Blank lines should seperate major points in the comments. In
** general, ask yourself the question, "If I didn't know what this
** code was, what it was for, how it fit in, etc., and if I didn't
** even have the documentation for it, would these comments be
** enough for me?"
**
** Some general guidelines for the code:
**
** ***** GENERAL SYNTAX *****
**
** - Commas and semicolons should always be followed by a space.
** Binary operators should be surrounded on both sides by
** spaces. Unary operators should be in direct contact
** with the object that they act on, except for "sizeof",
** which should be seperated by one space.
**
** - Two statements should never go on the same line. This includes
** such things as an if and the associated conditionally
** executed statement.
** In cases such as this, the second half of the if
** should be indented one tab stop more than the if. For
** example, use:
** if (cond)
** statement;
** never:
** if (cond) statement;
** or:
** if (cond)
** statement;
**
** - Braces ({}) should (almost) always be on a line by them-
** selves. Exceptions are closing a do, and terminating
** a struct definition or variable initialization. Braces
** should start at the same indent as the statement with
** which they bind, and code inside the braces should be
** indented one stop further. For example, use:
** while (cond)
** {
** code
** }
** and never:
** while (cond)
** {
** code
** }
** or:
** while (cond) {
** code
** }
** or:
** while (cond)
** {
** code
** }
** or anything else in that line. Braces which match
** should always be at the same tab stop.
**
** - Do statements must always be of the form:
** do
** {
** code;
** } while (cond);
** even if "code" is only one line. This is done so that
** it is clear that the "while" is with a do, rather than
** a standalone "while" which is used for the side effects of
** evaluation of the condition.
**
** - There should always be a space following a keyword (i.e.,
** for, if, do, while, switch, and return), but never
** between a function and the paren preceeding its
** arguments. For example, use:
** if (i == 0)
** exit();
** never:
** if(i == 0)
** exit ();
**
** - Every case in a switch statement (including default) should
** be preceeded by a blank line. The actual word "case" or
** "default" should have the indent of the switch statement plus
** two spaces. It should be followed by a space (not a
** tab) and the case constant. Multiple case labels on
** a single block of code should be on seperate lines, but
** they should not be seperated by blank lines. The
** switch statement should in general be used in place of
** such constructs as:
if (i == 1)
** code1;
** else if (i == 34)
** code2;
** else if (i == -1643)
** code3;
** which can be more succinctly stated as:
** switch (i)
** {
** case 1:
** code1;
** break;
**
** case 34:
** code2;
** break;
**
** case -1643:
** code3;
** break;
** }
** In point of fact the equivalent switch will compile
** extremely efficiently. (Note that if you had some
** instance where you could not use a case, e.g., checking
** for i < 5, else check for j > 3, else whatever, then
** the above ("if") code is in the correct style. However,
** if (i < 5)
** code1;
** else
** if (j > 3)
** code2;
** else
** code3;
** is acceptable.
**
** - A blank line should seperate the declarations and the code
** in a procedure. Blank lines should also be used freely
** between major subsections of your code. The major
** subsections should also have a block comment giving
** some idea of what is about to occur.
**
** ***** PREPROCESSOR USAGE *****
**
** - Fields of #defines and #includes should line up. Use:
** # define ARPA 25
** # define MAXFIELDS 18
** and not:
** #define ARPA 25
** #define MAXFIELDS 18
** Conditional compilation (#ifdef/#endif) should be used
** around all trace information, timing code, and code
** which may vary from version to version of UNIX. See
** the code below for an example of conditional compila-
** tion use.
**
** ***** VARIABLES AND DECLARATIONS *****
**
** - Defined constants (defined with the # define feature) must
** be entirely upper case. The exceptions to this are
** compilation flags, which begin with a lower case "x",
** and some sub-types for parser symbols. In any case,
** the majority of the symbol is upper case.
**
** - Global variables should begin with an upper case letter and
** be otherwise all lower case. Local symbols should be
** entirely lower case. Procedure names are all lower
** case. The only exception to this is the trace routine
** "tTf". You should avoid user non-local symbols (globals
** or # define'd symbols) which are one character only;
** it is impossible to distinguish them. Capitalization
** may be used freely inside global names so long as they
** are primarily lower case; for example, "ProcName" is
** an acceptable name (and preferred over either Proc_name
** or Procname).
**
** - Use descriptive variable names, particularly for global var-
** iables. "IEH3462" tells me nothing; nor does "R". On
** the other hand, "Resultid" tells me quite a lot,
** including what it might be, where I might go to see
** how it is initialized, etc. Try not to use variables
** for multiple purposes. Variable names like "i" are
** acceptable for loop indices & temporary storage
** provided that the value does not have long-term
** semantic value.
**
** - When the storage structure or type of a variable is
** important, always state it explicitly. In particular,
** include "auto" if you are going to take the address
** of something using the ampersand operator (so that
** some wayward programmer doesn't change it to register),
** and declare int parameters as int instead of letting
** them default.
**
** ***** GENERAL COMMENTS *****
**
** - It is quite possible to name a file "printr.c" and then
** put the code for "destroydb" in it. Try to arrange
** the names of your files so that given the name of a
** routine, it is fairly easy to figure out which file
** it is in.
**
** - Sometimes it is really pretty much impossible to avoid doing
** something tricky. In these cases, put in a comment
** telling what you are doing and why you are doing it.
** - Try to write things that are clear and safe, rather than
** things which you think are easier to compile. For
** example, always declare temporary buffers as local,
** rather than as global. This way you can another
** routine accidently when it still had useful info
** in it.
**
** ***** COMMENTS *****
**
** - The importance of comments cannot be overemphasised.
** INGRES is primarily a research vehicle rather than
** a program product. This means that there will be
** many people pouring over your code, trying to
** understand what you have done & modify it to do
** other things. Try to make life easy for them &
** maybe they will be nice to you someday.
**
** - Try to keep an idea of the flow of your program. Put
** block comments at the beginning of major segments,
** and use one-line comments at less major junctures.
** A person viewing your code at ten paces should be
** able to tell where the major segments lay.
**
** - The preferred format for block comments is to begin with
** a line containing slash-star alone, followed by a
** number of lines all beginning star-star containing
** the comment, and terminating with a line containing
** star-slash alone. Comments without the double-star
** at the beginning of each line should be avoided,
** since it makes the comments seemingly disappear into
** the body of the code.
**
** - The beginning of each routine should have a comment block
** in parametric form as demonstrated below. The fields
** "Parameters", "Returns", and "Side Effects" should
** be considered mandatory. Mark parameters as being
** (IN), (IN/OUT), or (OUT) parameters, depending on
** whether the parameter is used only to transmit infor-
** mation into the routine, in and out of the routine,
** or only to return information; the default is (IN).
**
** Remember, it is easy to write totally incomprehensible code in
** C, but we don't go in for that sort of thing. It isn't too
** much harder to write brilliantly clear code, and the code is
** worth a lot more later.
**
** For efficiency reasons, you should always use register variables
** when possible. A simple and extremely effective tip is to define
** a register variable, and assign an oft-used parameter to it,
** since it is particularly inefficient to reference a parameter.
** Another particularly inefficient operation is referencing arrays
** of structures. When possible, define a register pointer to the
** structure, and then say:
** struct xyz structure[MAX];
** register struct xyz *p;
** ...
** for (i = 0; i < MAX; i++)
** {
** p = &structure[i];
** p->x = p->y + p->z;
** (diddle with p->???)
** }
** and not:
** struct xyz structure[MAX];
** ...
** for (i = 0; i < MAX; i++)
** {
** Structure[i].x = Structure[i].y + Structure[i].z;
** (diddle with Structure[i].???)
** }
** Remember, the nice things about register variables is that they
** make your code smaller and they run faster. It is hard to
** lose with registers. There are three restrictions which you
** should be aware of on register variables, however. First,
** The only types which may be registers are int's, char's,
** and pointers. Second, there may only be three register
** variables per subroutine. Third, you may not take the address
** of a register variable (i.e., you may not say "&i" if i is
** typed as a register variable).
**
** Usage:
** example [flags] argument
**
** Positional Parameters:
** argument -- this gets echoed to the standard
** output.
**
** Flags:
** -n -- don't put a newline at the end.
** -x -- don't do anything.
** -b -- echo it with a bell character.
**
** Return Codes:
** 0 -- successful
** else -- failure
**
** Defined Constants:
** XEQ1 -- maximum number of simultaneous equijoins.
**
** Compilation Flags:
** xTRACE -- enable trace information
**
** Trace Flags:
** 5 -- general debug
** 6 -- reserved for future use
** Compilation Instructions:
** cc -n example.c
** mv a.out example
** chmod 755 example
**
** Notes:
** These comments don't apply to the code at all,
** since this is just an example program.
** Also, it is wise to avoid this many comments
** except at the head of main programs and
** at the head of major modules. For example,
** this sort of commenting is appropriate at
** the top of ingres.c (system startup) and
** view.c (virtual view subsystem), but not
** in small utility routines, e.g., length.c.
** This sort of routine should be limited to
** "Parameters:", "Returns:", "Side Effects:",
** and anything else that seems relevant in
** that context.
** A fairly large comment block should exist at the
** top of modules [files] which contain many
** procedures; this block should clarify why
** the procedures are grouped together, that
** is, their common purpose in life. A small
** block should occur at the top of each
** procedure explaining details of that proce-
** dure.
** Procedures should be on seperate pages (use the
** form feed character, control-L).
** A program will help you generate this comment block.
** In ex, go to the line where you want to insert
** a block and say "so /mnt/ingres/comment". It
** will ask you for a block type, e.g., "main"
** for main program, "modfn" for a file which
** contains only one function, "function" or
** "procedure" for a procedure within a module,
** "module" for a module header (e.g., as a
** seperate comment block for a major module
** [check .../qrymod/view.c for an example] or
** in header files.
** SCCS should be considered an essential tool, if only
** to maintain history fields.
**
** Deficiencies:
** It should handle pseudo tty's.
*/
/* the following macro is defined by <sccs.h> */
SCCSID(%W%); /* %W% is replaced by a version number by SCCS */
# define XEQ1 5
struct magic
{
char *name; /* name of symbol */
int type; /* type of symbol, defined in symbol.h */
int value; /* optional value. This is actually
* the value if it is type "integer",
* a pointer to the value if it is a
* string. */
};
struct magic Stuff;
main(argc, argv)
int argc;
char *argv[];
{
register struct magic *r;
register int i;
register int j;
int timebuf[2];
auto int status;
/*
** Note that in the declarations of argc and argv above, all
** parameters to any function should be declared, even if they
** are of type int (which is the default).
*/
r = &Stuff;
/* initialize random # generator */
time(timebuf);
srand(timebuf[1]);
/* scan Stuff structure */
for (i = 0; i < XEQ1; i++)
{
# ifdef xTRACE
if (tTf(5, 13))
printf("switch on type %d\n", r->reltype);
# endif
switch (r->type)
{
case 0:
case 1:
case 3:
/* initialize */
printf("hi\n");
break;
case 2:
/* end of query */
printf("bye\n");
break;
default:
/*
** be sure to print plenty of info on an error;
** "syserr("bad reltype");" would not have been
** sufficient. However, don't make syserr's
** overly verbose; they take much space in the
** object module, and it will probably be
** necessary to look at the code anyway.
*/
syserr("main: bad type %d", r->type);
}
}
/* resist the temptation to say "} else {" */
if (i == 5)
{
i++;
j = 4;
}
else
i--;
/* plot the results */
do
{
i = rand() & 017;
plot(i);
} while (j--);
/* wait for child processes to complete */
wait(&status);
/* end of run, print termination message and exit */
for (i = 0; i < 2; i++)
printf("bye ");
printf("\n");
}
** PLOT -- Plot a Bar-Graph
**
** Does a simple plot on a terminal -- one line's worth.
**
** Parameters:
** n (IN) -- number of asterisks to plot
**
** Returns:
** none
**
** Side Effects:
** none
**
** Deficiencies:
** Should allow scaling.
*/
plot(n)
int n;
{
register int i;
for (i = n; i-- > 0; )
{
printf("*");
}
printf("\n");
}
**************************************************************************
BBN PROGRAMMING STANDARDS FOR C
The following document was provided by Dan Franklin <DAN at BBN=UNIX>
in response to a general enquiry distributed to Arpanet BBOARDS:
Here's a short set of guidelines that represents a combination
of the thoughts of two groups within BBN. I was its last editor.
Hope you find it useful. If you come across something more
comprehensive (which this certainly isn't), I'd appreciate it
if you let me know.
PROGRAMMING STANDARDS AND PRACTICES
FOR THE UNIX COST CENTER
This memo proposes a set of practices that might be followed in
the cost center, in order to have a consistently organized and
written, readable, understandable, and ultimately maintainable
software package. Qualifying each of the items below is an (S)
or (P): the former indicates that, once agreed-upon, the rule
must be adhered to rigorously and consistently--a Standard; the
latter items are more of the flavour of "good programming
Practice"--once agreed-upon, programmers should follow the
practice to the extent that common sense dictates.
1. DOCUMENTATION OF ROUTINE INTERFACES
(S) Every routine should be prefaced by a standard header
which describes the routine and its interface precisely, so that
the routine could be used by someone without reading the code.
The rationale for such a header is that it concentrates the critical document-
ation in one place, making it easy for someone (including the original
designer/coder) to use the routine correctly, and allows for the possibility
of automatically stripping off all headers to form an Internal Documentation
document.
In some cases a routine may do a small amount of processing and then
call another routine. (As an example, a routine may provide a
"special case" interface to another more general routine with
a more cumbersome calling sequence.) In this case, a routine's header
may refer the reader to the header of another routine for further information.
The format suggested is illustrated below: comments are surrounded by
< > symbols. Any item that is irrelevant, e.g., no parameters,
no return value, no error conditions, may be omitted.
/*------------------------ E P R I N T -----------------------------------*/
/* <a brief description of the routine...also, put the routine name in
* caps in the line above. The description should give, in general
* terms, the routine's function, including an overall description of its
* parameters, return values, and effect on global databases. It should also
* describe any error conditions that may occur and be reflected to the
* caller. The return value must be described precisely.>
*/
int eprint(estrucp, istart, ilength)
struct estruct *estructp; /* Pointer to eprint's structure */
int istart; /* Place in structure to start from */
int ilength; /* # things in struct to print */
{
}
Note that each parameter gets its own declaration line, and has a
comment to convey its exact meaning.
Each file in a program or subsystem consisting of multiple files
should start with some additional information, giving (essentially)
the justification for putting all these routines in one file.
That is, it should include a description of the common function
they all perform, the related functions they all perform, the database
they have in common and which is contained only in this file, etc.
In the last case, especially, a detailed description of the database
should be included; otherwise, since the database is not part of any
one routine, it will not get described in any other comment field.
The detailed description may require reading comments associated
with the actual declarations in order to be complete.
This header should include a history list describing all modifications
to functions in this file. Each entry in the list should give the
person, the date, the names of the functions (or database elements)
changed, and the reasons.
For the file containing the main routine of a program, the following
standards should be observed:
1. The main routine should be the first routine in the file.
2. Before the main routine should appear an overall comment field
describing the calling sequence in some detail. Generally, the
program will also be described in a manual page as well; this
comment field need not duplicate the entire contents of the
manual page, but it should include information about the overall
structure of the program which would be useful to a maintainer.
3. The main routine should exit by calling exit(0) explicitly.
All programs, however, must return an exit status of zero on success,
and nonzero on error.
2. FORMATTING
(S) All programs must be indented, at least 4 spaces per
indentation level. Statements at the same nesting level should
be at the same indentation level; statements at deeper nesting
levels should be indented more. (For the purposes of this
standard, the keyword "else" is regarded as having the same
nesting level as the "if" it corresponds to.) For example, the
following indentation practices are not permitted:
if (condition)
statement-group;
if (condition)
statement-group; else
statement-group;
if (condition)
for (init; test; step)
statement-group;
if (condition) statement-group;
else statement group;
(S) Nesting the trinary conditional operator (?:) is not permitted.
(P) For internal formatting and indenting style, the following is recommended:
. 4-space indentations at each level;
. braces delineating blocks used in the style of
if (exp)
{
code
}
or
if (exp)
{
code
}
. a separate declaration statement for each pointer,
due to the confusion that can arise when reading
and modifying a declaration like
char *a, b, *c;
. comments lined up systematically;
. spaces between reserved words and their opening parentheses,
e.g., if (condition) rather than if(condition);
. no deep nesting (greater than 4 levels deep)
. statement blocks less than 1 page long
. one statement per line
. parentheses around the objects of sizeof and return.
(P) Concerning internal comments:
. Full English sentences are recommended.
. It is often clearer and more readable to make
small blocks of comments as "paragraph headers" to a block of
code than to comment every line or so, especially if the code
is straightforward and untricky (which most of it should be,
especially if the programmer is conscientious about variable
naming and datatype compatibility).
. Anything non-obvious should be well-commented.
(P) Concerning white space and other aids to readability and debugging:
. blank lines should be used between blocks of code that are
functionally distinct
. in expressions, operators should be surrounded by blanks
(but not operators which compose primaries, specifically
".", "->")
. in a function definition, the function name and its datatype
should appear on one line. Examples:
int pread(...)
SOMESTRUCT * function(param1, param2)
This latter practice aids readability and the comparison of
the definition of a routine with its uses.
(P) Other suggestions for improving readability:
. Side effects within expressions should be used sparingly.
It is recommended that no more than one operator with a side
effect (=, op=, ++, --) appear within an expression. Function
calls with side effects count as operators for this purpose.
. In an "if-else" chain, the form
if (condition)
statement;
else if (condition)
statement;
else if (condition)
statement;
should only be used when the conditions are all of the same basic
form: e.g., testing the same variable against different values.
If the conditions are qualitatively different, the additional
"if" statements should start on new lines, indented, as in
if (cond)
statement;
else
if (cond2)
statement;
else
statement;
. In the condition portion of an if, for, while, etc., side effects
whose effect extends beyond the extent of the guarded statement
block should be minimized. That is, in a block like
if ((c = getc(file)) != EOF)
{
guarded-stmts
}
other-stmts
it is natural to think of "c" being "bound" to a value only
within
the guarded-stmts; use of a variable set or modified in a condition,
outside the range of the statements guarded by the condition, is
quite distracting.
. The use of || and && with right hand operands having side effects
is discouraged. Also, whenever || and && are mixed in the same
expression, parentheses should be used for clarity.
3. MODULARITY
3.1. Routine Length
(P) Routines should be kept reasonably short. In general, a routine
(or command) processes one or more inputs and generates one or
more outputs, where each of the inputs and outputs can be concisely
described. Usually a routine should only perform one function. Signs
that a routine is too long, and ought to be split up, are: length
greater than two pages; heavy use of "internal" variables (variables
whose scope is less than the entire routine).
3.2. Shared Data and Include Files
(S) Declarations of variables that are to be shared among several routines
in a package should be placed at the beginning of the file containing
the routines. If they are entirely internal to the package, they should
be declared "static" to hide them from other files.
(S) Declarations of structures and global variables used for communications
between a package and its callers should be placed in an appropriately
named .h file. Shared global variables should be declared extern in the
.h file so that if the package which sets/uses them is accidentally not
loaded, a diagnostic will be issued by the loader.
(S) Definition and optional initialization of such globals should reside in
one and only one file for each program. If a global is clearly associated
with a specific package, it may be defined in the file for that package;
otherwise, it should be placed in a file called data.c which is
compiled and linked with the other routines when a program is
being built.
(P) Use of globals should be minimized by judicious use of parameters.
Note that while static and extern designators in C both yield "common"
storage allocation, static allows the named variable to be known only
within the context in which it is declared; thus, an identifier declared
static at the top of a file will only be known within the file.
In addition, local symbolic constants (#defines) may occur anywhere within
a routine, and may be placed below the #include directives if the writer
feels that this aids readability.
(S) Include files should contain all and only all of the following items
necessary for a given program and shared among two or more of its files:
. #defines
. typedefs
. extern declarations
(S) Include files may also contain nested #include directives. However, this
practice should be kept to a minimum. It is recommended that
include files which may be embedded in this way contain a check
to see whether they are being included twice, to avoid unnecessary
preprocessor and compiler complaints when the user includes them
both implicitly and explicitly. The simplest way to do this is to
check for a special hidden #define, as in
#ifdef _INCLUDEFILENAME
#define _INCLUDEFILENAME
.
. Contents of include file
.
#endif _INCLUDEFILENAME
3.3 Routine interfaces
(P) In general, a routine should be designed with a "natural",
easily-remembered calling sequence. Routines with more than
five arguments are not recommended. Routines with "op-code"
arguments, where one argument determines the interpretation
and functions of the others, are also not recommended (though
they may prove useful as internal routines to a package,
they should NOT be part of a package's documented interface).
4. PORTABILITY
(P) Adherence to datatype compatibility should be practiced where
reasonable. This can be facilitated by liberal use of C's typedef
facility and explicit casting of types, as well as by the
use of the union datatype.
(P) The following violation of strict adherence is permitted: a package
which returns a pointer to a structure whose format need not be known
outside that package may return a "generic" pointer, of type (char *).
It is recommended that the typedef PTR be used for this purpose; this
typedef will be provided in some standard system file. Note that char*
is specifically chosen because the language guarantees that any pointer
may be converted to a char* and back again without harm.
(S) Liberal use of #defines should eliminate "magic numbers", whether
machine dependent or implementation dependent or arbitrary/random.
(S) To the extent that the conditional compilation facility of C allows, non-
portable constructions can use this facility. Failing this, machine or
implementation dependent constructs should be visibly commented in the
code.
When using conditional compilation for portability purposes, be sure
to use appropriate parameters, rather than machine type, wherever
possible. For example, code which handles the C machine's 20 bit wordsize
or 10 bit bytesize should use the parameter BYTESIZE in cpu.h rather
than being ifdeffed on "mbb".
(P) Up to six register variables can be declared (with some effect)
on the C70 machine, but only three on the 11/70 (where additional ones
are assigned regular automatic storage). Therefore, the first three
register variables declared (in lexicographic order) should be ones
for which the most gain can be gotten.
Note that register variables, judiciously chosen, can be very good space/time
savers, but the compiler is not overly smart about them, and can give you
irritating "expression overflow" messages. In general, as with any
"optimization", it is wise to design, code and debug first, and then add in
register storage to one's declarations.
5. NAMING
(P) Names should be chosen for their mnemonic nature. Recall that although
there is a limit on the number of initial characters of a name that must
be distinct in C (and for a given machine), this does not prevent any
name from being longer if such length will aid readability.
(S) It is a useful C convention to use upper-case for #defines and typedef
names.
(S) One exception to the above is for parameterized #defines.
These may be in lower-case.
**************************************************************************
The following was provided via FTP by Keith A. Lantz at Stanford
<CSL.LANTZ at SU-SCORE> via FTP following the enquiry distributed via
Arpanet BBOARDS.
1
NETWORK GRAPHICS C STYLE SHEET
Andrew Shore, et al.
Computer Systems Laboratory
Stanford University
Stanford, CA 94305
18 June 1982
1. Names
Don't use capital letters in file names (except for V !?).
Avoid the underscore.
Global variables, procedures, structs, unions, typedefs, defined constants,
and macros all begin with a capital letter, and are logically capitalized
thereafter (e.g. MainHashTable). A global variable is one defined outside a
procedure, even though it may not be exported from the file, or an external
variable. The motivation for treating macros and constants this way is that
they may then be freely changed to procedure calls or (global or external)
variables.
Local variables begin with a lower-case letter, but are logically capitalized
thereafter (e.g. bltWidth, power, maxSumOfSquares). Fields within structures
or unions are treated in this manner also.
2. Comments
There are generally two types of comments: block-style comments, and on-the-
line comments. Multi-line, block-style comments will follow the UNIX style of
/* and */ appearing on lines by themselves, and the body of the comment will
start with a properly aligned *. The comment should usually be surrounded by
blank lines as well. The motivation for this is that it is easy to add/delete
first and last lines, and it is easier to detect the common error of omitting
the */ and thus including all code up to and including the next */ in a comment
(Yapp helps with that too).
/*
* this is the first line of a multi-line comment,
* this is another line
* the last line of text
*/
On-line comments are used to detail declarations, and to explain single lines
of code. And, I suppose, for brief (i.e. one line) block-style descriptive
comments.
1
This work was supported by the Defense Advanced Research Projects Agency
under contract number MDA903-80-C-0102.
2
Procedures are preceded by block-style comments, explaining their (abstract)
function in terms of their parameters, results, and side effects. Note that
the parameter declarations are indented, not flushed left.
/*
* Unblock:
* unblock the process pd
*/
Unblock( pd )
register Process pd; /* the process to unblock */
{
register Process *currPd,
*tmpPd;
register unsigned prio;
prio = pd->priority;
Disable;
pd->state = Ready;
/* Add pd to the ready queue */
currPd = (Process *) &ReadyqHead;
while ((tmpPd = currPd->link) != Null)
{
if (tmpPd->priority > prio)
break;
currPd = tmpPd;
}
pd->link = tempPd;
currPd->link = pd;
Enable;
}
3. Indenting
The above example shows many of the indenting rules. Braces ( "{" and
"}" )
appear alone on a line, and are indented two spaces from the statement they are
to contain the body of, the body is indented two more spaces from the braces
(for a total of four spaces). else's and else if's line up with their
dominating if statement (to avoid marching off to the right, and to reflect the
semantics of the statement).
3
if ((x = y) == 0)
{
flag = 1;
printf (" the value was zero ");
}
else if (y == 1)
{
switch (today)
{
case Thursday:
flag = 2;
ThursdayAction();
break;
case Friday:
flag = 3;
FridayAction();
break;
default:
OtherDayAction();
}
}
else
printf(" y had the wrong value ");
4. File Contents
I think we agreed on the following order for file contents.
1. initial descriptive comment (see example below) which contains:
a. file name, with indication of the relative path to it when
relevant
b. brief descriptive abstract of contents
c. a list of all defined procedures in their defined order
d. list of authors
e. list of current maintainers
f. list of recent and major modifications in reverse
chronological order with indication (initials) of who made the
change.
2. included files (use relative path names whenever possible)
3. external definitions (imports and exports)
4. function declarations (externals and forward references)
4
5. constant declarations
6. macro definitions
7. type definitions
8. global variable declarations
9. procedure and function definitions
Here is an example of the header comment:
/*
* FILE: libc/vkstuff/malloc.c
*
* CONTENTS:
* C storage allocator stolen and hacked up from UNIX for the SUN
* (NOTE: these were stolen and have the wrong naming conventions)
* malloc
* free
* realloc
* allock DEBUG ONLY!
* SetBrk ! our routine to fake up sbrk()
*
* AUTHORS: stolen and hacked by Andrew I. Shore (shore)
*
* MAINTAINER: shore
*
* HISTORY:
* 03/05/82 AIS replaced calls to UNIX sbrk() with calls to own version
* SetBrk() for the sun/Vkernel
*/
5. Parenthesis ()
We seem to have decided on the following conventions for parentheses. When
parentheses enclose the expression for a statement (if, for, etc.), the
parentheses `belong to' the expression, so there is a space between the keyword
and the parenthesized expression. For function calls the parentheses `belong
to' the call, so there is no space between function name and open paren (there
may some inside the parentheses to make the expression list (argument list)
look nice).
if (Mumble())
{
Grumble( (a = b) == 0 );
return (Nil);
}
else
{
Fumble( a, b, c );
return (ToSender);
}
5
It is unclear what to do with return. Note, then, that it is operators that
cause spaces on the outside of parentheses -- (a + b) * c.
6. General
1. One statement/declaration per line.
2. Make judicious use of blank lines.
a. At least 3 blank lines between individual procedures.
b. Blank lines surround "large" comments.
3. Make sure comments and code agree!
4. Don't just echo the code with comments -- make every comment count!
i.e. nix on:
/* add foo to bar */
bar += foo;
i
Table of Contents
1. Names 1
2. Comments 1
3. Indenting 2
4. File Contents 3
5. Parenthesis () 4
6. General 5
**************************************************************************