Skip to content

Coding Conventions

Any significant programming project will usually require developers to use a standardized set of coding conventions. These conventions might be set by a company, the leaders of an open-source project, or simply through historical precedent. Standardized coding conventions enable code written by multiple developers to be consistent and improves readability, maintainability, and extensibility. We have developed a simple set of coding conventions for ECE 2400 that we would like you to use in all programming assignments. Keep in mind that these are just guidelines, and there may be situations where it is appropriate to defy a convention if this ultimately improves the overall code quality.

Note that some of these conventions have been adapted from the Google C++ Style Guide. In general, anything not covered by the guidelines in this document should assume the Google style guide.

1. Directories and Files

This section discusses the physical structure of how files should be organized in a project.

1.1. Directories

All header, inline, data, and source files should be in a single src directory. All tests should be a in a single tests directory. Anything other than ad-hoc testing should always be done in a separate build directory.

1.2. File Names

Files should be named in all lowercase and should use a dash (-) to separate words. C source files should use the .c filename extension, and C++ source files should use the .cc filename extension. Header files should use the .h filename extension, and inline files should use the .inl filename extension. Data files that contain C/C++ code and are meant to be included using the C preprocessor should use the .dat filename extension. All test programs should end in -test.c. All evaluation programs should end in -eval.c.

1.3. Header and Inline Files

All header files should be self-contained. A header should include all other headers it needs. The definitions for template and inline functions should be placed in a separate .inl file and included at the end of the header. Every header should use include guards where the name of the include guard preprocessor macro is derived directly from the filename. For example, a header file named foo-bar.h would use the following include guards:

1
2
3
4
#ifndef FOO_BAR_H
#define FOO_BAR_H

#endif // FOO_BAR_H

2. Formatting

This section discusses general formatting that is common across all kinds of files.

2.1. Line Length

Lines in all files should in general be less than 80 characters. Using less than 74 characters is ideal since this is a natural width that enables reasonable font sizes to be used when using side-by-side code development with two listings on modern laptops and side-by-side code development with three to four listings on 24" to 27" monitors. Lines longer than 80 characters should be avoided unless there is a compelling reason to use longer lines to increase code quality.

2.2. Indentation

Absolutely no tabs are allowed. Only spaces are allowed for the purposes of indentation. The standard number of spaces per level of indentation is two. Here is an example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
int gcd( int x, int y )
{
  while ( y != 0 ) {
    if ( x < y ) {
      int temp = x;
      y = temp;
      x = y;
    }
    else {
      x = x - y;
    }
  }
  return x;
}

2.3. Vertical Whitespace

Vertical whitspace can and should be used to separate conceptually distinct portions of your code. A blank line within a block of code serves like a paragraph break in prose: visually separating two thoughts. Vertical whitespace should be limited to a single blank line. Do not use two or more blank lines in a row.

Do not include a blank line at the beginning and end of the function body in a function definition. So this is incorrect:

1
2
3
4
5
6
7
int foo()
{

  stmt1;
  return 0;

}

This is correct:

1
2
3
4
5
int foo()
{
  stmt1;
  return 0;
}

2.4. Horizontal Whitespace

Absolutely no tabs are allowed. Only spaces are allowed for the purposes of indentation. The standard number of spaces per level of indentation is two. In general, horizontal whitespace should be used to separate distinct conceptual "tokens". Do not cram all of the characters in an expression together without any horizontal whitespace.

There should be white space around binary operators. Here is an example:

1
2
int a = b*c;   // incorrect
int a = b * c; // correct

Try to use explicit parenthesis to make operator precendence explicit:

1
2
3
int a = a < 0 && b != 0;             // incorrect
int a = ( ( a < 0 ) && ( b != 0 ) ); // correct
int a = (a < 0) && (b != 0);         // correct

Sometimes there can just be too many paranthesis and just too much horizontal white space. So the third one might actually be more readable. Students will have to use their judgement.

In some cases, we should not include whitespace around an operator because the operator is not delimiting two distinct conceptual "tokens". Here are some examples:

1
2
3
4
5
6
7
8
int a = obj . field;  // incorrect
int a = obj.field;    // correct
int a = obj -> field; // incorrect
int a = obj->field;   // correct
obj . method( b );    // incorrect
obj.method( b );      // correct
obj -> method( b );   // incorrect
obj->method( b );     // correct

2.5. Variable Declarations

There should be whitespace around the assignment operator. Here is an example:

1
2
int a=3;   // incorrect
int a = 3; // correct

If possible, consder vertically aligning the variable names and assignment operators for related variables:

1
2
unsigned int a     = 32;
int*         a_ptr = &a;

Never declare multiple variables in a single statement. Always use multiple statements. Here is an example:

1
2
3
int a, b; // incorrect
int a;    // correct
int b;    // correct

2.6. Conditional Statements

if conditional statements should look like this:

1
2
3
4
5
6
7
8
9
if ( conditional_expression0 ) {
  statement0;
}
else if ( conditional_expression1 ) {
  statement1;
}
else {
  statement2;
}

Notice the use of spaces inside the parentheses since the () tokens should be conceptually separated from the conditional expression. We personally really like this style, but can also be okay to skip this horizontal whitespace like ths:

1
2
3
4
5
6
7
8
9
if (conditional_expression0) {
  statement0;
}
else if (conditional_expression1) {
  statement1;
}
else {
  statement2;
}

However, it is critical to be consistent! If you use curly braces for one part of an if/then/else statement you must use them for all parts of the statement. Avoid single line if statements:

1
2
3
if ( conditional_expression0 ) return 1; // incorrect
if ( conditional_expression0 )           // correct
  return 0;                              // correct

2.7. Iteration Statements

for loops should look like this:

1
2
3
for ( int i = 0; i < size; i++ ) {
  loop_body;
}

Notice the extra horizontal whitespace used to separate the parentheses from the initialization statement and the increment statement. We personally really like this style, but can also be okay to skip this horizontal whitespace like this:

1
2
3
for (int i = 0; i < size; i++) {
  loop_body;
}

However, it is critical to be consistent! We really want the open curly brace should be on the same line as the for statement.

2.8. Function Definitions

Function definitions should look like this:

1
2
3
4
int foo_bar( int a, int b )
{
  function_body;
}

We encourage inserting space inside the parenthesis. We personally really like this style, but can also be okay to skip this horizontal whitespace like this:

1
2
3
4
int foo_bar(int a, int b)
{
  function_body;
}

However, it is critical to be consistent! Notice that for functions the open curly brace goes on its own line. Do not insert a space between the function name and the open parenthesis. So this is incorrect:

1
2
3
4
5
// incorrect
int foo_bar ( int a, int b )
{
  function_body;
}

2.9. Function Calls

Function calls should usually use whitespace inside the parenthesis. For example:

1
int result = gcd( 10, 15 );

If there is a single argument, sometimes it may be more appropriate to eliminate the whitespace inside the parenthesis. Or if it is more readable it might be fine to do this if the arguments are very simple.

1
int result = gcd(10,15);

Again, the focus is on readability.

3. Naming

3.1. Type Names

For C programs, the names of user-defined types should usually be all lowercase, use underscores (_) to separate words, and use a _t suffix.

1
typedef unsigned int uint_t;

For C++ programs, the names of user-defined types should usually use CamelCase.

1
2
3
4
class FooBar
{
  ...
};

When specifying pointer types, the * should be placed with the type without whitespace:

1
2
3
int * a_ptr; // incorrect
int *a_ptr;  // incorrect
int* a_ptr;  // correct

As a reminder, never declare multiple variables in a single statement. This is never allowed:

1
int *a_ptr, *b_ptr; // not allowed!

3.2. Variable Names

The names of variables should always be all lowercase with underscores (_) to separate words. Do not use CamelCase for variable names. For pointers, use a _ptr or _p suffix. For data member fields, use a m_ prefix. While single letter variable names are common in the lecture examples, single letter variable names should be very rare in real code.

3.3. Function/Method Names

The names of free functions and methods should always be all lowercase with underscores (_) to separate words. Do not use CamelCase for function or method names.

4. Comments

Though a pain to write, comments are absolutely vital to keeping our code readable. The following rules describe what you should comment and where. But remember: while comments are very important, the best code is self-documenting. Giving sensible names to types and variables is much better than using obscure names that you must then explain through comments. When writing your comments, write for your audience: the next contributor who will need to understand your code. Be generous — the next one may be you!

Do not state the obvious. In particular, don't literally describe what code does, unless the behavior is nonobvious to a reader who understands C/C++ well. Instead, provide higher level comments that describe why the code does what it does, or make the code self describing.

4.1. Comment Style

Use // comments. These are perfectly acceptable now in C99. Do not use the older /* */ comments. Include a space after // before starting your comment:

1
2
//without space, incorrect formatting
// with space, correct formatting

4.2. Comment Location

Avoid trailing comments. They make lines too long and are hard to read. Prefer placing each comment on its own line whenever possible. So avoid this:

1
2
3
4
5
6
if ( a > b ) { // if a is greater, subtract b
  c = a - b;
}
else { // if b is greater, subtract a
  c = b - a;
}

Prefer this instead:

1
2
3
4
5
6
7
8
9
// if a is greater, subtract b
if ( a > b ) {
  c = a - b;
}

// if b is greater, subtract a
else {
  c = b - a;
}

4.3. File Comments

All files should include a "title block". This is a comment at the very beginning of the file which gives the name of the file and a brief description of the purpose and contents of the file. Title blocks should use the following format:

1
2
3
4
//=========================================================================
// foo-bar.h
//=========================================================================
// Description of the purpose and contents of this file.

The horizontal lines used in the title block should extend exactly 74 characters (i.e., two '/' characters and 72 = characters). You do not need to duplicate comments between the .h and .cc. Often the header will have a description of the interface, and the source file will discuss the broad implementation approach.

4.4. Function Comments

Almost every function declaration in the header should have comments immediately preceding it that describe what the function does and how to use it. These comments may be omitted only if the function is simple and obvious. These comments should be descriptive ("Opens the file") rather than imperative ("Open the file"); the comment describes the function, it does not tell the function what to do. In general, these comments do not describe how the function performs its task. Instead, that should be left to comments in the function definition.

Every function definition in the source file should have a comment like this:

1
2
3
4
//------------------------------------------------------------------------
// foo_bar()
//------------------------------------------------------------------------
// optional high-level discussion of implementation approach

4.5. Old Comments

Do not leave old comments in the source file. So you must remove comments that were provided by the instructors.

5. Scoping

This section discusses use of local and global variables.

5.1. Local Variables

Place a function's variables in the narrowest scope possible. C99 no longer requires all variables to be declared at the beginning of a function, so declare variables close to where they are initialized.

5.2. Static and Global Variables

Do not use non-const static or global variables unless there is a very good reason to do so. Const global variables are allowed and should definitely be used instead of preprocessor defines.

6. C Pre-processor

Using the C pre-processor should be avoided. Use of the C pre-processor should usually be limited to include guards and the UTST macros. When the C pre-processor must be used, pre-processor macro names should be in all capital letters and use an underscore (_) to separate words.

Do not use the C pre-processor to declare global constants. Use const global variables instead.

7. Examples

Here is an example of an incorrectly formatted for loop:

1
2
3
for (int i = 0; i < n; i ++){
  a += c;
}

There should be a space inside the parenthesis and no space between i and ++. There should be a space after the closing parenthesis and the open curly brace. Here is the same code formatted correctly:

1
2
3
for ( int i = 0; i < n; i++ ) {
  a += c;
}

Here is an example of an incorrectly formatted if statement:

1
2
3
4
5
6
if (a < 0 && b != 0){
  c = 1 / c;
}
else (x % 2 == 0){
  ...
}

There should be a space inside the parenthesis and we need extra parenthesis to make the operator precedence more explicit. We also need a space between the closing parenthesis and the open curly brace.

1
2
3
4
5
6
if ( ( a < 0 ) && ( b != 0 ) ) {
  c = 1 / c;
}
else ( ( x % 2 ) == 0 ) {
  ...
}

Once we have multiple levels of nested parenthesis, it might be more readable to do something like this:

1
2
3
4
5
6
if ( (a < 0) && (b != 0) ) {
  c = 1 / c;
}
else ( (x % 2) == 0 ) {
  ...
}

Here is an example of a poorly formatted return statement:

1
2
3
4
5
6
void foo()
{
  ...
  return bar( x )
  * bar( y );
}

Indentation should be used to make this more clear:

1
2
3
4
5
6
void foo()
{
  ...
  return bar( x )
       * bar( y );
}

This code does not include spaces around the assignment operator, and isn't even consistent in its formatting:

1
2
3
double foo= b;
int c =bar;
double e=1;

This should look like this:

1
2
3
double foo = b;
int    c   = bar;
double e   = 1;

Notice how we lined up the variable names and the assignment operators vertically.

Here is an example of incorrectly formatted code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
int gcd(int x, int y){
  while(y!=0)
    {

      if ( x < y){
        int t=x; x= temp; x =y;
      }
        else
          x = x - y;
    }


     return x;
}

Here is an example of correctly formatted code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
//------------------------------------------------------------------------
// gcd()
//------------------------------------------------------------------------

int gcd( int x, int y )
{
  // iterate until GCD is found
  while ( y != 0 ) {

    if ( x < y ) {
      // swap x and y
      int temp = x;
      y = temp;
      x = y;
    }
    else {
      x = x - y;
    }

  }
  return x;
}