COMP 3400 Coding Standards

This document provides standards for indenting, commenting, and otherwise formatting your C code. COMP 3400 is unusual because there is a specific, external style that you must adhere to, namely the GNU standards for C.

Even though this standard is likely different from (and possibly uglier than) any standards you've adhered to in the past, you are required to use it for this course for the following reasons:

  1. It's a real standard used for GNU projects, including a huge number of open-source projects.
  2. You might be forced by your employer to adhere to some arbitrary standard in the future.
  3. Being able to adopt a particular standard as needed is a good skill for a professional software engineer.

Tools

Even if you hate this standard, two things will help you:

  1. A style checker is part of the testing framework for the three projects, so you will instantly know if you're generally conforming to the standards.
  2. There's a tool called clang-format that can automatically format your code for you!

Let's say you have a file called program.c that you want to format. The easiest way to use clang-format is as follows:

clang-format --style=gnu -i program.c

Using this command will format program.c to GNU style. The -i flag does the formatting in-place, meaning that the input file is changed. If you're worried about what clang-format will do to your code, you can use it without this flag, which will send the output to standard out, allowing you to redirect it to a new file, as follows:

clang-format --style=gnu program.c > program.gnu.c

Then, you can review the new file and decide whether or not to replace the original. Please read the man pages for more information about clang-format options.

Spaces vs. Tabs

There's a long-standing, quasi-religious debate over whether code should be indented with spaces or with tabs. Both sides have points in their favor. Because of the Java IDEs we use, you're probably used to tabs.

Well, GNU style demands spaces, two spaces, in fact, to indent anything. Visually, this can be a little bit of an adjustment, but your development practices don't need to change much. Although tab (\t) is a character, it doesn't have to be the character that is inserted into your document when you press the tab key on your keyboard. Most development environments allow you to specify that hitting the tab key can produce a tab or a specific number of spaces. For those of you using gedit, go to Preferences and check the Insert spaces instead of tabs box in the Editor tab. You can also check the Enable automatic indentation box to automatically inserts spaces for you. Finally, you can change the tab width to 2 spaces. All good text editors will have similar settings.

Braces and Indentation

In GNU style, pairs of braces should be in the same column, one below the other, unlike the Java standard of putting opening braces on the same line as the header.

Any block of code should be indented. Such blocks include functions, selection statements, and loops. As we said above, the GNU style requires an indentation of 2 spaces. If you're indenting a single line, that's all you need to do. However, the GNU style has a wrinkle that requires selection statements and loops to have their braces indented and the contents of those braces indented again.

Another peculiarity of GNU style is that the return types of functions are written on the line before the function name, making the function name the first thing on a line.

Do This Don't Do This
int
main (int argc, char **argv)
{
  /* Code here */
  return 0;
}
int main (int argc, char **argv) {
	/* Code here */
	return 0;
}
for (i = 0; i < 10; i++)
  printf ("%d\n", i);
for (i = 0; i < 10; i++)
	printf ("%d\n", i);
for (i = 0; i < 10; i++)
  {
    printf ("%d\n", i);
  }
for (i = 0; i < 10; i++) {
	printf ("%d\n", i);
}
if (i < 10)
  {
    printf ("%d\n", i);
  }
else
  {
    printf ("Nope\n");
  }
if (i < 10) {
	printf ("%d\n", i);
} else {
	printf ("Nope\n");
}
switch (input)
  {
  case 1:
    printf ("1");
    break;
  default:
    printf ("2");
  }
switch (input) {
	case 1:   printf ("1");
		break;
	default:  printf ("2");
}

Naming Conventions

When naming functions and variables, use all lowercase with an underscore ('_') to separate words. This approach is called snake case, in contrast to the camel case preferred in Java. Always pick meaningful names and types.

Do This Don't Do This
size_t bytes_read;
char *incoming_message;
int b;
char *myIncomingMessage;

Comments

Each function should also have a comment explaining it, with additional comments every few lines, especially when those lines are confusing. Many variables declarations will also require some explanation, but simple counters will not. If a comment adds no additional information, it isn't valuable.

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, don't write paragraphs of comments. Use a brief sentence or two in strategic places that make it easier for people to understand your code.

Other Formatting Issues

Avoid Long Lines

All lines of code should be no longer than 80 characters wide. Long lines of code should be broken and wrapped to the next line. The continued line should be indented in a natural alignment.

Do This
some_variable = a_really_long_variable_name + another_variable +
                yet_another_really_long_name;
int
calculate_sum (int first_value, long second_value, int third_value,
               unsigned int last_value)
{
  /* ... */
}

Avoid Long Functions

Restrict functions to two screens (about 100 lines of code) and never have more than three levels of indentation. Code that is longer or uses more levels of indentation is typically too complex to maintain.

Don't Do This
if (...)
  {
    while (...)
      {
        if (...)
          {
            if (...)
              {
                for (...)
                  {

Spacing after Functions and Keywords

When making a function call, put a space between the function name and the opening parenthesis as well as one after each comma. Similarly, put a space after keywords like for, if, switch, and while. Some styles differ on this spacing rule, but using it will help you match the skeleton code you're given, keeping all the code consistent.

Do This Don't Do This
printf ("hello %s from %s\n",
        to_person, from_person);
printf("hello %s from %s\n",to_person,from_person);

Return on Errors

Return from functions when checking for errors rather than using if-else blocks. Doing so reduces levels of indentation.

Do This Don't Do This
if (value < 0)
  {
    fprintf(stderr, "bad value\n");
    return 1;
  }
/* Code using value here */
if (value >= 0)
  {
    /* Code using value here */
  }
else
  {
    fprintf(stderr, "bad value\n");
    return 1;
  }

Defensive Coding

A number of practices can make your code safer or easier to debug.

Initialize Local Variables

If you don't provide an explicit initial value for local variables, the variable will be created with an unpredictable, random value. This could cause unexpected behavior, which may be different every time you run the program. If you don't know what to set it to, 0, false, or NULL are often reasonable options. The compiler flag -Wall should warn you when your code uses a variable that might not be initialized.

Do This Don't Do This
int value = 0;
printf ("%d\n", value);
int value;
printf ("%d\n", value); // Who knows what will print?

Pointers and Allocation

After calling free() to free a pointer, immediate follow up by setting the pointer to NULL. After calling malloc() to get dynamically allocated data, validate the allocation was successful by checking that the pointer is not equal to NULL. When using malloc() to dynamically allocate an array, use sizeof() instead of making assumptions about data sizes.

Do This
free(pointer);
pointer = NULL;
int *data = malloc(SIZE * sizeof(int));
if (data == NULL)
  return ALLOCATION_ERROR; // Error depends on situation

Use Assertions

If there's a condition that should never be false (making very bad, unpredictable behavior occur), include an assert() in your code to check the condition. Recall that you'll need to #include <assert.h>. For instance, assume that a function takes an integer parameter input, which should only be between 1 and 5, inclusive. At the top of your function, you should have a statement like the following.

Do This
assert(input >= 1 && input <= 5);

Use Newlines for Debug Prints

Whenever you're writing debug code, be sure that it includes a newline (\n). Otherwise, there's no guarantee that it will be printed on the screen before the program crashes because the printf() family uses buffered output. This guideline seems narrow and specific, but many talented programmers have wasted hours of their lives thinking that their code was crashing earlier than it did.

Do This Don't Do This
// Newline guarantees print
fprintf (stderr, "Before danger\n"); 
dangerous_action ();
fprintf (stderr, "After danger\n");
// Might not print before crash from dangerous_action()
fprintf (stderr, "Before danger");
dangerous_action ();
fprintf (stderr, "After danger");

Avoid Global Variables

Global variables should be thought of as a last resort (meaning almost never used), as the values can change from one function call to the next. Using them adds a great deal of confusion to code, especially recursion. Global variables are also less thread-safe.

Define Constants

Use #define for constants as appropriate. The name of the constant should be in all uppercase. Using constants allows you to change the value in a single location if you need to.

Do This Don't Do This
#define QUESTIONS 5 // Probably at the top of the file
for (question = 0; question < QUESTIONS; question++)
  {
    /* Stuff here */
  }
for (i = 0; i < 5; i++)
  {
    /* Stuff here */
  }

Credits

These coding standards are adapted in large part from the CS 361 Coding Standards, written by Michael S. Kirkpatrick. This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.