COMP 2400 Coding Standards

This document provides standards for indenting and commenting your C and C++ programs. These standards are intended to help you keep track of your code and help the instructor understand and grade your code. Proper indentation allows you to step through code more easily. The presence of comments helps explain your code to yourself and anyone else who must read it. The instructor will take points off for programs that do not follow this coding convention.

Braces and Indentation

Any block of code should be indented. Such blocks include the bodies of classes, functions, selection statements, and loops. A more controversial question is where the surrounding curly braces should go. The guiding principle is consistency: You should always follow the same pattern in your code. Any of the three following styles is acceptable.

Note that C allows braces to be omitted in the case of single line if statements, else statements, and loops. Many experienced programmers will always omit braces in this case to make code shorter. Some experienced programmers insist on using braces in all circumstances to avoid logic errors if a second line of code is added later. As a beginner programmer, it is wise to use braces always, but eventually you should make a thoughtful decision about whether you want to keep doing so.

K&R style - Original

The K&R style, named for its use in Kernighan and Ritchie's book The C Programming Language, puts the opening brace on the same line as the header. Furthermore, the ending brace of an if statement is on the same line as the matching else if there is one. A variant of this style is commonly seen in the Java API. Many software developers prefer this style because it takes up very little space. This original K&R style put the opening brace of a function in C on its own line, but the Java API variant treats methods like like any other block.

int main(int argc, char **argv)
{
	int annoyance = 0;
	int i = 0;
	char song[1024] = "";
	for (i = 1; i < argc; ++i)
		strcat(song, argv[i]);
	sing(song, &annoyance);
	printf("Annoyance: %d\n", annoyance);
}

void sing(char *song, int *annoyance)
{
	if (test(song)) {
		printf("No Stairway allowed!\n");
		++(*annoyance);
	} else {
		printf("*sings \"%s\"*\n", song);
		*annoyance = max(0, *annoyance - 1);
	}		
}

int test(char *song) 
{
	return strcmp(song, "Stairway to Heaven") == 0;
}

K&R style - Stroustrup variant

A very slight difference from the original is the Stroustrup variant, named for Bjarne Stroustrup, the creator of C++. This variant puts the opening brace of a function on the same line as its header and always puts a closing brace on its own line instead of crowding the else. Another feature of the variant is that very short functions are sometimes put on a single line.

int main(int argc, char **argv) {
	int annoyance = 0;
	int i = 0;
	char song[1024] = "";
	for (i = 1; i < argc; ++i)
		strcat(song, argv[i]);
	sing(song, &annoyance);
	printf("Annoyance: %d\n", annoyance);
}

void sing(char *song, int *annoyance) {
	if(test(song)) {
		printf("No Stairway allowed!\n");
		++(*annoyance);
	}
	else {
		printf("*sings \"%s\"*\n", song);
		*annoyance = max(0, *annoyance - 1);
	}		
}

int test(char* song) { return strcmp(song, "Stairway to Heaven") == 0; }

Allman style

The Allman style, named after Eric Allman, always puts braces on their own lines. This style makes the beginnings and ends of blocks very clear, but the cost of this clarity is code that takes up more space. Another difficulty with this style is that some editors that automatically add braces put the opening brace on the same line as the header of a block.

int main(int argc, char **argv)
{
	int annoyance = 0;
	int i = 0;
	char song[1024] = "";
	for (i = 1; i < argc; ++i)
		strcat(song, argv[i]);
	sing(song, &annoyance);
	printf("Annoyance: %d\n", annoyance);
}

void sing(char *song, int *annoyance)
{
	if (testString(song))
	{
		printf("No Stairway allowed!\n");
		++(*annoyance);
	}
	else
	{
		printf("*sings \"%s\"*\n", song);
		*annoyance = max(0, *annoyance - 1);
	}		
}

int test(char *song) 
{
	return strcmp(song, "Stairway to Heaven") == 0;
}

Organization and Whitespace

Preprocessor directives should generally be kept at the top of your files. Next should come function prototypes followed by the body of your main() function and other function bodies. At least one blank line should separate each function. Avoid global variables.

Strategically placed blank lines to separate sections of code within functions can improve readability. However, blank lines should not be used indiscriminately, excessively, or without clear purpose.

Comments

All files should include the following header information.

/*******************************
 *
 *  Project Name: ...
 *  Description: ...
 *  Date: Project due date   
 *  Authors: Names of team members
 *
 *******************************/

Write your comments in complete sentences or at least in complete thoughts. Avoid ambiguous, overly short comments (one- or two-word comments), and avoid commenting the obvious. An output operation (such as a printf()) is often clear, but an arithmetic or string operation may not be. On the other hand, do not write paragraphs of comments. Use a brief sentence or two in strategic places that make it easier for people to understand your code.

Variable Declarations

Variable declarations should generally be accompanied by a brief description of what the variable is used for. This can be done with one or two lines of comments, usually after the declaration but before the end of the line. If your comments are too long to fit on the line, please put them above the variable declaration. Please observe how good variable names reduce the need for long comments and make code clearer. Some variables, such as loop counter variables, need not be commented.

Examples

  • int age; // Student's age
  • // Thermal energy transfer crossing unit area per unit time
    float transferEnergy;
  • FILE* inputFile; // Input file for WAV data

Camel case (starting each variable name with a lowercase letter and each subsequent word inside the variable name with an uppercase letter) is not universally followed in C as it is in Java, but it is still a good practice. Likewise, using all capital letters (separated by underscores) for constants is a widely used practice in both languages.

Variable names in Java have essentially no limitations on length. Variables in C are usually limited by the compiler, often to 31 or 63 characters. Regardless, either number is plenty of characters for a descriptive name.

Conditionals

Conditional branching statements should include one or two sentences describing what you are trying to accomplish and any variables that are involved in the testing of the conditionals.

Examples

// Decide which description of a person to print,
// based on age. 
if (age < 13) 
{ 
	printf("young kid"); 
} 
else if (age >= 13 && age <= 19) 
{ 
	printf("teenager"); 
} 
else 
{ 
	printf("adult"); 
} 

// Decide which arithmetic operation to perform based on
// operator. 
switch (operator) 
{ 
	case '+': 
		printf("plus"); 
		break; 
	case '-': 
		printf("minus"); 
		break; 
	case '/': 
		printf("divided by"); 
		break; 
	case '*': 
		printf("times"); 
		break; 
	default:
		break; 
} 

Loops

A loop should have one or two sentences explaining in general terms what you are trying to accomplish in the loop, and how you terminate the loop.

Example

int i = 0; 
int sum = 0; 
int arraySize = 100;

// Sums up the positive integers in an array. 
for (i = 0; i < arraySize; ++i)  
{ 
	// Skips negative numbers. 
	if (array[i] < 0) 
		sum += array[i];         
} 

Functions

C and C++ require function prototypes, unlike Java. The function header comments should be followed by the function body. Do not put these comments with the prototype. Good function header comments should describe, in one or two sentences, what the function does. They should also explain the parameters and the return value. Let's look again at the code for adding up positive integers given above, implemented as a function, with headers.

Examples

The following code packages up the array summing code given before into a function.

/* 
   Description:	Sums up the positive integers in an	array. 
				
   Parameters:	array     - the array of integers to be summed
		arraySize - the size of the array 
				
   Returns:	sum of the positive integers
*/ 

int positiveSum(int array[], int arraySize) 
{ 
	int i = 0; 
	int sum = 0; 
	int arraySize = 100;

	// Sums up the positive integers in an array.
	for (i = 0; i < arraySize; ++i)  
	{ 
		// Skips negative numbers. 
		if(array[i] < 0) 
			sum += array[i]; 			
	}
	
	return sum;
} 

Here is a function that returns more than one value by using pointer arguments. Such a function may affect the memory locations sent via the pointers and may also return an explicit value. The example function below swaps two integer values if they are different. It returns true if the swap is performed and false otherwise.

/* 
   Description:	Swaps two integers if they have different values.
				 
   Parameters: 	first 	- a pointer to the first integer
		second 	- a pointer to the second integer
				
   Returns:	true if the values were swapped and false otherwise 
*/

bool swap(int *first, int *second) 
{ 
	if (first && second && *first != *second) // NULL and equality checks
	{
		int temp = *first;
		*first = *second;
		*second = temp;
		return true;	
	}
 
	return false; 
} 

Structs

A struct is a complex, programmer-defined data type, similar to classes in Java. Please include at the top of the struct declaration one or two sentences explaining what the struct is used for. Also, each data member of the struct should be clearly identified in a similar manner to variable declarations. An exception can be made here: If the variable name chosen is clear and helpful, it may obviate the need for commenting each data member variable.

Examples

// This struct describes a car, which has a make (a company),
// a model, and the year. 
struct Car 
{ 
	char* make;
	char* model;
	int year;
}; 

// This struct describes a student, has a first name, middle initial
// (if any), a last name, an address at college, an address at home, 
// a list of lab grades, test scores, and homework grades. 
struct Student 
{ 
	char* firstName; 
	char middleInitial; 
	char* lastName; 
	struct Address collegeAddress; 
	struct Address homeAddress; 
	int labGrades[15]; 
	int testScores[5]; 
	int homeworkGrades[10]; 
}; 

Miscellaneous

Please note that good variable names reduce the need for documentation, or even make it redundant, as in the Car struct example.

Some programmers use the convention of prepending the data type of the variable to the actual variable name. For example, a string variable firstName might be written sFirstName, or a variable sum might be written iSum if it is an integer sum or fSum if it is a floating point sum. Older Microsoft code favors a complex system of doing so called Hungarian notation. This convention is not encouraged in this class, but you should be aware of it in case you encounter it in the real world. Development environments and compiler tools make these kinds of conventions less important. In languages like Java and C# which are strongly typed, these conventions have almost no value since you will be prevented from putting the wrong type in to a variable. In languages like C, a case can be made for these conventions, but they are far from necessary and make code harder to read.

Text that is wrapped around is difficult to read in older (especially command-line) editors. If you have a line of code that exceeds 80 characters, you should consider breaking it up into two shorter lines that are properly indented. C generally treats the newline character as white space, and looks for a semicolon (;) to mark the end of a statement. One notable exception is a string literal that is too long to fit on one line. You can splice a long string literal by inserting a backslash (\) character, followed by the return key stroke, followed by the rest of the string on the next line. The C preprocessor will delete each occurrence of the backslash character followed by a newline.

Practices to Avoid

Many practices should be avoided because they make the code harder to read or increase the likelihood of bugs. Below we have a short (and non-exhaustive) list of such practices.

Using numbers in place of character literals

It's true that every character literal in C has an ASCII value that can be interpreted as an integer. These values can be added, subtracted, and compared against other numbers. Some programmers are tempted to use the number instead of the literal. Doing so is always wrong. The result is less readable, and it's easy to get the wrong value.

if (character >= 97 && character <= 122)		// Don't do this!
	printf("%c is a lower case letter!", character);

Always use the character literal, given between single quotes, to represent the character.

if (character >= 'a' && character <= 'z')	// Much clearer
	printf("%c is a lower case letter!", character);

Using break or continue in loops

The keywords break and continue can be used, respectively, to leave a loop immediately or start the next iteration. They should be avoided so that the loop has only one entry point and one exit point. Otherwise, the path of execution taken through a loop becomes harder to reason about. It is always possible to rewrite a loop so that all important termination conditions are checked at the top. Consider the following example.

bool isPalindrome = true;
int length = strlen(word);
for (int i = 0; i < length/2; ++i)
	if (word[i] != word[length - i - 1])
	{
		isPalindrome = false;
		break;	// Don't do this!
	}

In this case, the break keyword is being used for efficiency. There is no need to keep checking additional characters once we know the word is not a palindrome. Instead, this check can be incorporated into the loop condition as follows.

bool isPalindrome = true;
int length = strlen(word);
for (int i = 0; i < length/2 && isPalindrome; ++i) // Condition included here
	if (word[i] != word[length - i - 1])
		isPalindrome = false;

Systems programmers might argue that there are some cases where a break or a continue might make a difference in terms of performance. Empirically, these arguments do not usually hold. Furthermore, an optimizing C compiler might ultimately treat both pieces of source code the same.

Using pow() to square values

Unlike some languages, C does not provide exponentiation as a built-in numerical operator. Instead, the function pow() can be used to raise arbitrary bases to arbitrary powers. However, pow() should not be used to square numbers. It's less efficient and less readable to write pow(x,2) instead of x*x. Furthermore, pow() takes double values and returns a double value. Thus, it cannot be used to square or cube int values without converting from a double back to an int.

Writing your own code instead of using libraries

Avoiding pow() when squaring numbers is a special case. In general, you should use the standard C libraries rather than writing your own code. There is no need to write a general function to raise values to powers when pow() already exists. The example above that tests to see if a character value is between 'a' and 'z' would be better replaced by islower().

There are a few exceptions to this rule. Because you're here to learn, your professors may often require you to write code for a problem that is already solved by a library. In these cases, you must follow your professor's instructions, even if there is a simple way built into the standard library.

Misusing shortcuts

Incrementing a variable in C is a common operation. Expressions like i++ and ++i pop up so often that it's easy to forget exactly what they mean. Programmers occasionally forget that they are both shorthand for i = i + 1 and begin to think of them as a fancy way to write i + 1. When confused, a programmer might write something like the following.

int i = 14;
i = i++;

At first glance, it may appear that the second line of code really means i = i = i + 1. Assigning i an extra time is pointless, but it does no harm. However, remember that the postfix version gives back a copy of the original value, before it has been incremented. In this case, i will be incremented, but then its original value will be stored back into itself. In the code given above, the final value of i is probably still 14, but the result can differ depending on the C compiler you use.

Using lowercase L or uppercase O as variables

You should not use lowercase L (l) as a variable. Depending on your editor, it can be almost indistinguishable from the number one (1). Four levels of nested loops are uncommon, but when they happen, programmers are tempted to use i, j, k, and then l. Use any other single letter instead of l.

Uppercase O (O) is similarly objectionable because it looks like the number zero (0). If you are following conventions for making local variables, you would never create a variable starting with an uppercase letter in the first place, but even lowercase O (o) throws off the eye.

Credits

Many of these coding standards are based on a document originally created by Pedro Morales.