The source code for programming projects should always be organized and written with the future tasks of testing, debugging and maintenance (possibly by others) in mind. These tasks will be easier if the project is well organized and the source code is written in a clear and consistent manner.
This document describes some basic rules for C coding and project organization. Some general aspects of the portability problem are also addressed.
Each project directory must have README and Makefile files. The README file should give a general overview of the project and the files that implement it. It should provide the information about author, the short description of the project and list of all files belonging to the project.
All source code files (*.c and *.h) should conform to the following general commenting standards:
/** * Copyright (C) 2010 John Smith (cse111111@cse.yorku.ca) * CSE_no: cse111111 */
/** * The functions in this file solve the classic * towers of Hanoi problem. */<
/** * ``main'' manages the command line interface to solving * the towers of Hanoi problem. The command line arg * (which must be string representations of numbers) * indicate the number of disks to be moved and the source * and destination tower numbers. (The towers are * identified with the numbers 1, 2 and 3.) * * @param argc the number of command line arguments * @param argv a pointer to an array of strings where: * There must be exactly 3 arguments where: * -- the fist arg is the number of disks to move * -- the second is the ID-num of the source * -- the third is the ID-num of the destination * @return always returns an exit code of 0. */ int main(int argc, char * argv[]) {}
You may noticed that the convention used for the public comments, specifically the
/**
(with the extra *
) and the tags @param
and @return
, correspond to Java commenting standards. In particular
javadoc can parse these specially formatted comments and the following
declaration to produce nicely formatted HTML documentation automatically. While
there is no version of javadoc for C code at this time, it does no harm
to use the clear convention of Java in your C code1.
While the public documentation should be written so that it does not require the reader to understand or even look at the implementation, private documentation is meant to help the reader understand the actual C code implementing a function. The comments should be written under the assumption that the reader is a competent C programmer. For example:
i++; /* Increment i by one */is a useless comment since it is entirely obvious to a C programmer.
Often, no private comments are required at all in well written programs. The use of descriptive variable and function names is also a great help. Using descriptive names often eliminates the need for comments (private) Consider:
foo = foo->bar; /* move "foo" to next item */
The comment would be unnecessary with the more intelligent variable and field names:
item = item->next;
#define
preprocessor directive. (It is usually preferable to use an
enum for a small number of integers instead of a #define
.)
For example, do not write code like:
double x = 3.14159265358979323846*2.6*2.6;
for(i = 32; i < 212; i += 2)
if ((j = foo()) == 2)
instead, use:
#include <math.h> /* This defines the value of PI */ #define RADIUS 2.6 double x = M_PI*RADIUS*RADIUS;
/* Note following temperatures assume Farenheit scale */ #define FREEZING 32 #define BOILING 212 #define TEMP_INCREMENT 2 for(i = FREEZING; i < BOILING; i += TEMP_INCREMENT)
typedef enum {FooGood = 0, FooWarn = 1, FooBad = 2} FooReturn_t; if ((j = foo()) == FooBad)
#ifndef FOO_H #define FOO_H /* Body of foo.h with (possibly) other #includes... */ #endif /* FOO_H */
Asserts should not be used as a way of informing end-users of predictable error conditions in the operation of a program. Rather, they should be used mainly during the development stage to help the programmer figure out where things are going wrong.
Despite this, the source code often uses asserts in this ``lazy'' way.
There is one coding ``standard'' that is commonly used but is incorrect and may lead to portability problems. In particular, there are occasions where the following assumptions are made:
void *
is the same size as an integer (int
) and data of one type can be cast to the other.
void *
and a pointer to a function void *()(...)
are the same size and either can be cast to the other. Both of these assumptions violate the formal specifications in the ANSI C standard. They are, however, very commonly encountered.
Using these convention makes some of the code easier to write and more readable. Note that there is NO assumption about the size of these things. Normally, however, they are all either 16 or 32 bits.
Some of the problems explore ways to avoid these assumptions.
Some of the source code follows some other arbitrary conventions that are a matter of personal choice. These include:
funcP = &foo
to set funcP to be a pointer
to the function foo(). Other programmers can use the shorter and equivalent
form: funcP = foo
.