C/C++ Programming Conventions Prof. Thomas Narten
Duke University CS DepartmentLast Update: September 15, 1996
There are three major sections in this note: (1) program submission guidelines, (2) general programming standards, and (3) naming conventions for variables in source programs.
all target builds your program from sources,
leaving the executable in the file a.out. The all target
should be the default target, executed when make is invoked
with no arguments.
clean target removes all files that can
be rebuilt from sources, including .o files, a.out, or
those generated by such programs as lex and yacc.
lint target runs lint on the
source files of your program. A lint target is unneeded for
C++ programs, or on systems where lint is unavailable. When
gcc is used, ``gcc -fsyntax-only -Wall'' can be used in place
of lint.
turnin
command. Turnin takes the names of the files being submitted for its
arguments. Your submission must include all files needed by
make above.
The following guidelines improve the readability of source programs. (To simplify adhering to these guidelines, emacs elisp modes are available.)
/* * <filename> -- <brief title and/or purpose> * * programmer -- <you> * * date -- <creation date> * * modification history * * <date> <change made> * */
The modification history section is used to record changes made to an
otherwise stable module.
Finally, comments are only useful if they are concise and make sense. Assume that the reader knows a programming language and is an experienced programmer. Write comments in such a way that if you were to look at the program one year from now, you would be able to figure out what it does and how it works.
/* * ================================================================ * <name> -- <brief statement of action performed in * terms of input parameters and value returned.> * ================================================================ */
The procedure comment should not be more than two or three lines. If you feel you need more lines, your procedure is probably doing too much. Separate procedures by one blank line. Be sure to indicate return values (if any) along with side effects such as modified global variables or pointer parameters used for returning values.
#includes and header (.h) files to hold data definitions
used in multiple source files. Header files should contain only
definitions and no declarations. Avoid including source code via
#include; use separate compilation instead.
#define (or the const keyword) to define any
constant (numeric, character, and string) that has meaning apart from
its literal value. Most numbers other than 0 or 1 fit into this
category.
goto's and other constructs that make the flow of control hard
to follow.
for (i = 0; sb[i] ~= ' '; i++)
/* null */;
for header), and place the closing brace on a line by
itself, lined up with the first non-blank character on the line
of the statement containing the matching opening brace.
For example,
if (c <= cMax) {
GetToken (c);
c++;
}
else clause of a simple if statement lines up with the
if, whereas the closing brace lines up with the if for
multi-statement clauses. Some examplesif (fCheck)
/* single statement */
else ...
if (fCheck) {
...
} else ...
if (fCheck) {
...
} else if (i > 0) ...
if statements with return,
break, etc. statements rather than if-then-else
clauses to filter out terminating cases.
if (fCheck) {
return(1); /* or break, exit, etc. */
}
/* main code */
...
is easier to follow than
if (!fCheck) {
/* main code */
} else {
return(1); /* or break, exit, etc. */
}
make, GNU Emacs, and
rcs. Familiarity with a debugger (e.g., gdb) is
essential (e.g., just after a core dump).
if statements, put the return value into a variable and use
assert(3) to check it.
perror to print meaningful error messages after failed
system calls. A statement such as
fp = fopen(``/etc/passwd'', ``r'');
if (fp == NULL) {
perror(``/etc/passwd'');
return(-1);
}
indicates which file could not be opened (and why), in contrast to the
following:
fp = fopen(``/etc/passwd'', ``r'');
if (fp == NULL) {
fprintf(stderr, ``Can't open file\n'');
return(-1);
}
free to return unneeded memory
previously acquired via malloc.
This section describes the methods for creating names for procedures and variables.
Where possible, name procedures by the (single) action they perform. In most cases, the name should be a verb or verb phrase. Capitalize words that appear in the name (for example, GetToken).
Variable names should be mnemonic. That is, the name of a variable should relate to some property of the values that variable can contain. We shall name variables by the abstract type of their values.
An abstract type is a set of values on which a collection of operations can be performed.
For example, a file descriptor is an abstract type. Values of type file descriptor refer to files and can be used in certain i/o operations. However, multiplication and division on file descriptors has little (or specialized) meaning.
Programs often deal with physical properties of objects, such as length, time, or mass. The units of each of these properties define an abstract type, such as inches, minutes, or grams. Relative quantities also define abstract types. For example, values that describe the position of a point relative to one origin are of a different abstract type than values that describe the same point relative to a different origin.
An abstract type is not tied to a hardware representation in the same way that an ``ordinary'' programming language type is, but instead allows more precise expression of the meaning of a variable.
For example, C and Pascal allow any integer variable to be used as an array index of any array. Yet, an array can usually be considered a mapping from a value in a specific domain (the index) to a value in a specific range (the contents of the indexed location). To index an array with a variable that is not of the domain type of the array is either an error or a programming technique that should be used sparingly.
In the convention we will use, an array is named according to the abstract types of its domain and range. Thus, it is easy to tell (1) if the array is being indexed with a value of the proper type, and (2) if the value of the array is being used in the proper context.
So, how are variable names constructed?
Each simple abstract type used in a program is given a unique tag, a two-letter abbreviated name.
For example, the tag for a character is
ch. The tag for a string is sb (for string block).
Compound types, such as arrays, are given names constructed from the types
of their components. For example, since an array is a map, array
names start with the tag mp, followed by the tag of the domain,
followed by the tag of the range. Thus, an array that maps characters to
strings is named mpchsb.
A record (struct in C) defines a new type, so it is given its own
tag. For example, a structure for defining a student record might be
called an sr and be defined as
struct sr {
int id; /* student id */
char *sb; /* student name */
};
Occasionally, two different variables of the same type appear within the same scope. If this happens, a qualifier is appended to one or both of the variables. A qualifier is a short name that further defines the use or purpose of the variable. The first character of a qualifier is capitalized.
For example, if a procedure is reading characters until a special end of
file character is read, the characters may be read into a variable named
ch and compared to the character in the variable named chEOF.
The naming convention provides type construction rules or schemas that show how to construct compound types from simple types. A large program typically has only a few (say 10-20) simple types. This ability to quickly and easily construct names of variables based on the types they contain is the real power of the convention.
Let X and Y denote arbitrary tags (two-character
mnemonics for simple types). Each of the following schemas shows how
to construct a new type from the given simple type.
pX
X. If * is the indirection operator, then *pX
is an X. For example, if ch is a character, then pch is
a pointer to a character, and *pch is a character again.
cX
X. If ch is a character, then cch is a
count of characters.
dX
X. X+dX is an X.
mpXY
X and range Y. mpXY[X] is
a Y.
iX
X.
rgX
mpiXX, array with domain iX and range X.
lX
X. Useful when allocating memory
dynamically for records. (This constructor is not usually needed in C.
It can usually be replaced by the built-in sizeof operator.)
XMin
X. For all X, X
XMin.
XMax
X. For all X, X <
XMax. Note that XMax is not actually of type X
(since it is larger than all X). If XMin = 0 then
XMax is the cardinality of the domain of mpXY.
XMax = 0 means the domain is empty.
XMac
X (Mac is Maximum
Allocated). Used when X is the domain of a
dynamic data structure, such as a stack, to indicate the maximum
allocated extent. For all X, X < XMac;
XMac
XMax. If XMac = 0 then no elements
are on the stack.
XFirst
X in the interval,
X
XFirst.
XLast
X in the interval,
X
XLast. The empty interval is represented by XLast
< XFirst.
XNil
X to represent the empty instance. May only be
compared for equality or inequality.
f
fEOF might be true if
end-of-file has been reached on a certain file.
ch
w
i
sb
string block). Although represented in C
as a pointer to a block of characters (rgch), strings occur so
frequently it is often more convenient to think of them as members of
a primitive type.
fd
fp
FILE; a stream).
pch
cch
ich
rgch).
cchMax
rgsb
mpchch
mpchsb
mpchsb[ch]
mpchsb[sb]
mpchsb is ch, not sb.
rgch
argc and argv, are called csb and
rgsb.
dpy
Display *dpy).
wn
Window wn).
gc
int gc).
co
unsigned long co).
ev
XEvent ev).
xc
int xc).
yc
int yc).
pd
int pd).
This document is based heavily on one created by John T. Korb, Department of Computer Science, Purdue University. Original reference: Charles Simonyi, Meta-Programming: A Software Production Technique, pp. 34-45, Xerox PARC technical report CSL-76-7.
This document was generated using the LaTeX2HTML translator Version 0.6.4 (Tues Aug 30 1994) Copyright © 1993, 1994, Nikos Drakos, Computer Based Learning Unit, University of Leeds.
The command line arguments were:
latex2html -split 0 main.tex.
The translation was initiated by Thomas Narten on Sun Sep 15 17:13:05 EDT 1996