Introduction
There are many different styles when it comes to the art of writing computer programs. Some people, for example, tend to be very wordy in their program--long variable names, page-long comments, and so on. Unfortunately, most people go to the other extreme--small variable names and no comments. This document is an attempt to describe a set of guidelines which you will be expected to follow on any program you write for the CS faculty here at Transy. The goal is to produce programs which are readable not only by the computer that is interpreting your programs but by other humans as well.
Programming is rarely done in a vacuum. Other people will need to read and understand your code. Of course, if you are ``still around'' to answer questions, this is not always a big problem. But, if the author is not around, then only the code itself can speak. It can also be a problem even if it is yourself trying to decipher code six months after you have written it. Good programming style will help to alleviate some of the pain.
The style which we are suggesting is a blend of several different techniques. It focuses on these points:
Naming of functions
Function names should be descriptive of what they do. This means that many times you will need a function name made up of multiple words. Since our languages do not allow spaces in a name, we have to develop an alternative to indicating word boundaries. For this, we use one of the following convention. One possibility is to separate the multiple words with underscore characters. For example:
... int Calculate_Student_Average () { ... }Many folks feel that this is a little unwieldy, both in writing it and in reading. A different approach is to start the name with a lowercase letter; then, each new word in the name should begin with a capital letter.
... int calculuateStudentAverage () { ... }Pick a style which appeals to you and follow it consistently.
Naming of variables
Variables follow the same naming convention as function names, with one exception. Indices used in iteration (loops) will many times be a single character. The convention is to use i for a loop, j for a nested loop inside of that, k for another level, and so on.
Commenting
The rules for commenting can be summed up in two sentences:
... int i; // sets up an integer variable called i ...On the other hand, the next comment tells the user something which they do not immediately know and so is a ``good'' comment:
... int count; // keeps track of incoming data records ...On the third hand, there is often no need to comment, if the choice of function names and variable names has been reasonable:
... int numberOfStudents; // keeps track of the number of students ...Global commenting
Since we encourage modular programming, there is rarely the need to comment at a global level, except for a couple of instances. First, each file which is part of the project should have a comment at the beginning which identifies the name of the file, the project which is it part of, the people who worked on the code, the date it is due, the purpose of the code in general, and any bugs which the programmers have identified. Second, in the event that you find a good use for a global variable, any such variable which you use needs to be commented as to its purpose and in what files it is used.
Separation of code, header files, and makefiles
For simple programs, you will find that one file is sufficient for all the work you are doing. However, when the programs begin to grow in complexity, a more modular approach is preferred.
There are two broad categories of files in C and C++: source files and header files. Program files contain the code which makes the magic happen. Header files are used to ``help'' the system work its magic. For example, header files will often contain the function prototypes which are necessary for the program to work. They may also contain various constants which you have defined for the program.
One of the most important things which you should find in the header file are the comments which describe what each function is supposed to do. The information should include the date that the function was written (or last modified), who has worked on it, what the inputs are, and what kind of output to expect. This provides a kind of summary of everything going on in your code. So, someone else can use this as a roadmap to figure out what functions need to be called to accomplish X, Y, or Z, and what parameters need to be passed into those functions.
Each header file which you write will be associated with one or more source files. These are the ones which end in .c, .cpp, or .cxx. In the early days of your CS education, it will not be uncommon for your entire program to fit into one such file. However, as the programs grow in complexity and you spend a great deal of time maneuvering around a huge text file, you will discover the benefits of splitting the source code up into more manageable chunks.
Let's say that you are writing a compiler. There's a well-defined translation stage and a well-define execution stage. Rather than lumping all of the functions needed for these two stages together into one large and unwieldy file, why not break the program into three pieces. One file will be all of the translator stuff. We'll call that one translator.cpp. There will be things that it needs to have from header files that just apply to this one file. So, we will have a header file called translator.h. Similarly, we will write execution.cpp and execution.h. There will also need to be some header material which both files share. Rather than duplicating it, we'll create compiler.h which both files will include. Finally, we need a third source file which will contain the ``glue'' that holds it all together. Since we try to be descriptive, let's call this last file compiler.cpp. This will be the file that contains the main function which runs everything.
Of course, there are countless ways to do this project. We could have stayed with two files, for example, and made a standalone translator and a standalone executor. Then, the need for the compiler.cpp file would be eliminated. Remember, there is no one ``right'' way to do things--find a system that is consistent and helps the reader understand what is going on and you will be fine.
Function commenting
As pointed out, the header will contain a comment for each function prototype explaining who wrote it and what the input and (expected) output are. This will allow the reader to quickly understand the purpose of each function in your program.
Any block of code which may not be clear should also be commented. What you want to indicate to the reader is why something is being done, not how it is being done.
White space
There is one final aspect to the readability of a piece of code--the correct use of white space. By this, we mean two things. First, you should indent various blocks of your code. Second, you should separate various blocks with blank lines. These two elements will help to make your code more readable.
Indentation does not seem like it would be very important. But, it can make a vast difference in the way a program looks. The same goes for blank lines--after all, they're blank, what information can they convey! But, consider the following:
#include <stdlib.h> #include <iostream.h> void main () { int i; char name[50]; cout << "Please enter your name" << endl; cin >> name; for (i=0;i<10;i++) { cout << "Pleased to meet you, " << name << endl; } }Contrast that to:
#include <stdlib.h> #include <iostream.h> void main () { int i; char name[50]; cout << "Please enter your name" << endl; cin >> name; for (i=0;i<10;i++) { cout << "Pleased to meet you, " << name << endl; } }It is obvious to the reader in the second example that the program is made up of one function which consists of three sections. The variable declarations are laid out at the top. Then comes a section which does input. Finally, a block of code prints the message 10 times.
Blocks of code which do distinct things should be separated by blank lines. Any control structure should have its elements indented--this includes functions, for loops, while loops, if statements, and so on. Those are the two rules to remember when writing programs.
One of the nice things about using a ``modern'' editor like XEmacs is that it will do a lot of this work for you. XEmacs is aware of dozens of different languages. When you start working on a program called foo.cpp, for example, XEmacs knows that it is a C++ program. If you press the tab key, the editor will automatically indent what is considered a reasonable amount in that language. By the way, XEmacs will also help you do things like match parentheses and braces. It will also highlight with color different pieces of a language (all comments in one color, all keywords in another, an so on). Of course, one of the authors of this paper is an avid fan of XEmacs, so consider this a biased push for it.
Minimal requirements of any program
Finally, beyond the style, there is a certain level of competence which we expect from you.
The code you produce has to be syntactically correct in order to be
run by the computer. But, it is equally important that it be readable by
humans. Use good function and variable names. Don't be afraid of white
space and comments. And don't try to teach the language. Your professors,
other programmers, and yourself are all people who will one day need to
look at your code and figure out what it is supposed to be doing. Give
them some help, OK?