This page discusses various ideas and suggestions for commenting code. First, you should consider reading the paper The fine Art of Commenting. This is a nice introduction to commenting code, giving numerous examples together with a good discussion.
For ECE 250, you are required to comment your code. I wrote the following guidelines after realizing that many students are simply told: "comment your code" without giving a clear indication as to what constitutes a valid comment or what information is necessary.
Any useful program must be commented, and of course, all programs in ECE 250 are useful (if, for no other reason, for your performance in the course). Commenting is generally helpful, even in a 2nd-year course like ECE 250 for a number of reasons:
This document is divided into a number of sections:
The following are recommendations for how you can comment your code. The idea behind comments is to help a new reader of the code to understand it. You will find that, if you look at your own code, after ignoring it for one month that you, too, will be a new reader of that code. It is not required that you follow this description, for example, you may prefer JavaDoc style comments, or you may have been exposed to an alternate style of commenting on one of your previous Co-op Work Terms.
The first guideline for commenting code appears not in the comments, but in the naming of class, function, parameter, and variable. The names should be descriptive but not overly verbose. Code which makes use of this is sometimes call self-commenting code, however, you should not expect this to be the only form of commenting.
In some cases, despite the comments of others, I believe that N, int i, and int j are perfectly good variables under certain circumstances. For example, in setting all the entries of a matrix (two-dimensional array) to zero, compare:
// set all the entries in the adjacency matrix to 0.0 int i, j; for ( i = 0; i < N; ++i ) { for ( j = 0; j < N; ++j ) { adjacency_matrix[i][j] = 0.0; } }
and
int row, column; for ( row = 0; row < N; ++row ) { for ( column = 0; column < N; ++column ) { adjacency_matrix[row][column] = 0.0; } }
As you are aware from ECE 150, C#, C++, and Java have two styles of comments:
Use the block (/* ... */) style of comments to describe a class or function, but use the in-line (// ...) style of comments to add comments inside a class or function. The justification is simple: block (/* ... */) comments may be nested around an arbitrary number of in-line (// ...) this allows you to comment out an entire function simply by placing block (/* ... */) comments around it.
This example shows how we can comment out the function math::sin which uses in-line comments
/* double math::sin( double x ) { double result = 0.0; // the Taylor series converges quickly if |x| < pi/2 if ( std::fabs( x ) < std::PI/2 ) { . . . } . . . return result; } */
whereas, if you were to use /* ... */ inside the function, then only part of the function would be commented, for example:
/* double math::sin( double x ) { double result = 0.0; /* the Taylor series converges quickly if |x| < pi/2 */ if ( std::fabs( x ) < std::PI/2 ) { . . . } . . . return result; } */
If you try compiling the above code, the error will not appear until after the last (now unbalanced) comment.
One of the poorest forms of comments is to place a comment at the end of a line. On occasion, where it is necessary to explain what a single in of code does, such a comment is acceptable, however, in general, this leads to poor formatting, irregular indentation once code is modified, and a maintenance nightmare. It also leads to lines of code being commented (often unnecessary) as opposed to blocks of code being commented (often useful). Consider, for example, a block of code:
...; // A comment ...; // A second comment for ( ... ) { ...; // A comment ...; // Another comment if ( ... ) { ...; // A comment about this line ...; // but no comment about the ...; // purpose of the if statement } ...; // Some more comments ...; // See how nicely lined up these ...; // comments are? Do I get a bonus? }
Once this code has gone through numerous revisions, it will probably look something like:
...; // A comment if ( ... ) { ...; // A second comment ...; // A new comment } for ( ... ) { ...; // Another comment ...; // Yet another comment if ( ... ) { if ( ... ) { ...; // A comment about this line ...; // A comment breaking the other comment... } ...; // but no comment about the ...; // Needed another comment here... ...; // purpose of the if statement } else { ...; // A new comment ...; // Another new comment } ...; // Some more comments if ( ... ) { ...; // See how nicely lined up these ...; // No, these comments are not nicely ...; // lined up. No bonus. } ...; // These comments aren't making much sense now... ...; // comments are? Do I get a bonus? }
I wish I could show some student code which does exactly this. Even worse, if tabs are used, the size of a tab may change (this may be set by editors) which leads to some bizarre results (as comments which use tabs for lining them up end up being unaligned):
...; // A comment ...; // A second comment for ( ... ) { ...; // A comment ...; // Another comment if ( ... ) { ...; // A comment about this line ...; // but no comment about the ...; // purpose of the if statement } ...; // Some more comments ...; // See how nicely lined up these ...; // comments are? Do I get a bonus? }
Placing comments at the ends of lines also encourages laziness (easy to do after-the-fact: "Oh, here's a line I can comment...") and also encourages exceptionally stupid comments, as is demonstrated here:
++j; // increment j
We will use the term documentation to describe the comments which appear before a class or function to give a general overview of its purpose.
Each class should be be documented by comments which contain at least a significant subset of the following information:
/* * Class_name * * Douglas Wilhelm Harder * 2009-04-19 * * What the class does, what is its purpose, etc. * Freude, schöner Götterfunken, Tochter aus Elysium, * wir betreten feuertrunken, Himmlische, dein Heiligtum! * Deine Zauber binden wieder, was die Mode streng geteilt: * alle Menschen werden Brüder, wo dein snafter Flügel weilt. * * Member Variables: * type variable What this variable does. * Any restrictions on its value. * * Class Variables: // static variables <- comments about comments :-) * type variable What this variable does. * Any restrictions on its value. * * Class Constants: // static const variables * type variable What this constant does. * * Member Functions (Accessors): * type function_name( type param_1, ... ) const * One- or two-line summary of what this function does. * * Member Functions (Mutators): * type function_name( type param_1, ... ) * One- or two-line summary of what this function does. * * Class Functions: // static functions * type function_name( type param_1, ... ) * One- or two-line summary of what this function does. * * Bugs: // if applicable * Todo: // if applicable * References: // if applicable (very useful) */ template <typename T> class Class_name { private: // Member Variables type variable; // Class Variables static type class_variable; public: // Class Constants const static type CONSTANT; // Accessors type get( type param_1, ... ) const; } // Mutators type set( type param_1, ... ); // Class Functions static type class_function( type param_1, ... ); };
References are very helpful when documenting code. If possible, give a reference to other documents which more clearly state what the intention of the function or class is, especially if it is in reference to design documents.
Note that some companies may prefer that you never use the /* ... */ comments, instead, using the more consistent, if less appealing:
///////////////////////////////////////////////////////////////////// // Class_name // // Douglas Wilhelm Harder // 2009-04-19 // . // . // . // Bugs: // if applicable // Todo: // if applicable /////////////////////////////////////////////////////////////////////
Each function should be documented by comments in the form:
/* * type Class_name::function_name( type param_1, type param_2 ) const * * Douglas Wilhelm Harder (if different from the class) * 2009-04-19 (if different from the class) * * A description of what the function does, what is its purpose, what * it returns, etc. * Wem der grosse Wurf gelungen, eines Freundes Freund zu sein, * wer ein holdes Weib errungen, mische seinen Jubel ein! * Ja, wer auch nur eine Seele sein nennt auf dem Erdenrund! * Und wer's nie gekonnt, der stehle weinend sich aus diesem Bund! * * Preconditions: * Are there any conditions which must be satisfied for this * function to be run? For example, a linked list must be not * be empty for a pop operation to occur. * * Postconditions: * Are there any conditions or restrictions on the class after * this function is executed? * * Parameters: * type param_1 * Description of the parameter and any * restrictions on its value. * * type param_2 * Another description and any * restrictions on its value. * * Bugs: // if applicable * Todo: // if applicable * References: // if applicable (very useful) */
The above may appear to be overkill for shorter functions. Clearly, a function such as int size() const which just returns a stored value need not have a novel written about it. In such a case, the the comments could be abbreviated to:
/* * int Single_list::size() const * * Returns the number of objects in the linked list. * * No pre- or postconditions and no parameters. * * Returns: * The number of objects in the linked list. */ templateint Single_list ::size() cosnst { return count; }
If you are petitioning your source code into blocks of similar or associated functions, a more decorative comment is sometimes useful:
/********************************************************************* * Sub Section * * Freude trinken alle Wesen an den Brüsten der Natur, * alle Guten, alle Bösen folgen ihrer Rosenspur. * Küsse gab sie uns und Reben, einen Freund, geprüft im Tod; * Wollust ward dem Wurm gegeben, und der Cherub steht vor Gott. *********************************************************************/
or
/********************************************************************* * ***************************************************************** * * * * * * * Sub Section * * * * * * * ***************************************************************** * *********************************************************************/
or even
/********************************************************************* * * * ************************************************************* * * * * * * * Sub Section * * * * * * * ************************************************************* * * * *********************************************************************/
As each of these use block comments, you clearly should not use this to partition the statements within a class or a function.
Within a function, any block of code consists of a sequence of statements, possibly preceded by some initialization code and followed by clean-up code. The description or overview of the sequence of statements which defines a function should be commented on in the function documentation.
After that, the sequence of statements in a block of code can usually be broken down groups of statements which perform one particular task. These can be proceeded by a few comments describing the task that they are accomplishing. These statements may include simple statements as well as more complex loops and conditional statements.
Some straight-forward examples of commenting blocks of code include:
// swap array entries i and j T tmp = array[i]; array[i] = array[j]; array[j] = tmp;
// set all the array entries, except the first, to 0 int i; array[0] = 1; for ( i = 1; i < N; ++i ) { array[i] = 0; }
If you want to comment a single line of code, you can always add the comment after the line. If a number of lines contain comments, it is usually better if you line the comments up. (Example needed...)
What you must not comment are obvious lines of code:
++count; // add one to count
If you add comments like this, you will loose marks for commenting.
Some people believe it is a good idea comments to the right of the actual code instead of above it. For many reasons, this is unacceptable (see how not to use end-of-line comments). In general this will be a maintenance nightmare.
Blocks of commented code should be separated from each other. The following is an example of code submited in ECE 250. Note how the comments and statements are all grouped together? It is difficult to understand the flow.
//arrays.......................................... int oneArray [size()]; //array.......................................... double arrayZeroIn [vertices_count()]; int positions [size()]; //the.......................................... int vertices_left = vertices_count(); //make............................................. for(int j = 0; j < vertices_count(); j++) { inDegreesCopy[j] = inDegreesArray[j]; } //it............................................. for(int i = 0; i < vertices_count(); i++) { ///integer.......................................... //the.......................................... int j = 0; //store.......................................... //and.......................................... //in the positions array for(int l = 0; l < vertices_count(); l++) { if(inDegreesCopy[l] == 0) { arrayZeroIn[j] = (prioritiesArray[l]); positions[j] = l; j++; } } //find.......................................... //in-degree.......................................... int minPosition = 0; for(int l = 0; l < j; l++) { if(arrayZeroIn[l] < arrayZeroIn[minPosition]) { minPosition = l; } } minPosition = positions[minPosition]; //print.......................................... cout << minPosition; vertices_left--; //print........................................... if(vertices_left != 0) { cout << "-"; } //decreasing............................................. for(int k = 0; k < vertices_count(); k++) { if(matrix -> get((minPosition),k) == true) { inDegreesCopy[k] -= 1; } } //the start....................................... //infinity........................................ //from.......... inDegreesCopy[minPosition] = int(INF); }
In general, most non-trivial functions begin with some form of initialization (e.g., where memory is allocated, various data structures are initialized, etc.) and some form of clean up (e.g., where memory is deallocated). Thus, you could begin with:
type Class_name::function_name( type param_1, type param_2 ) { // Initialization . . . . . . . // Clean Up . . . return value; }
The clean-up code may occur somewhere within the body, for example:
type Class_name::function_name( type param_1, type param_2 ) { // Initialization . . . . . for ( ... ) { . . if ( ... ) { // Clean Up . . . return value; } } }
In most cases, a conditional statement should be preceded by a comment describing what the statement tests and does. If the conditional statement is reasonably trivial, it operation may be described in comments describing the block of code containing the conditional statement.
The comments of an if statements must indicate what is being tested, perhaps with a comment as simple as // if X then do Y The size of the comment should be proportional to what is being done.
// test boundary conditions on n if ( n < 0 || n >= array_size ) { throw illegal_argument(); }
// find the maximum entry in the // array and assign it to max int i; int max = array[0]; for ( i = 1; i < N; ++i ) { if ( array[i] > max ) { max = array[i]; } }
The comment for repetition statements (either for loops or while loops) should reflect the complexity. If the repetition statement simply covers a range of values, state so, for example
// For each entry in the two arrays, // set the entries to their default values int i; for ( i = 0; i < array_size; ++i ) { array[i] = 0; visited[i] = false; }
however, if the array is more complex, then it should be described appropriately. If the loop potentially terminates early (using either a break or a return statement), then this should be noted in the comments.
// Iterate through all the entries of the linked list, // starting at the list head // Terminate early by returning false if something is found. SingleNode<T> *ptr; for ( ptr = list_head; ptr != 0; ptr = ptr -> next() ) { // Check if the current node stores the object if ( ptr -> retrieve() == obj ) { return true; } } return true;
Logic also dictates that comments should go around any any initialization code for the loop. For example,
// Find the location and value of the maximum // entry in the array, and assign these to // posn and max, respectively. int i; int posn = 0; int max = array[0]; for ( i = 1; i < array_size; ++i ) { if ( array[i] > max ) { max = array[i]; posn = i; } }
The example sin.cpp demonstrates an implementation of a numerical calculation of sin(x) for double-precision floating-point numbers using Taylor series and trigonometric identities. The output of this function is given in the file sin.out.