CS 361 Coding Standards

In professional software development settings, organizations typically have a set of coding standards, which are rules you are expected to follow regarding your style. Groups enforce these standards in a variety of ways, including the absolute rejection of any piece of code that deviates from this style. Here are the coding standards that you are required to adhere to in CS 361. Failure to adhere to these requirements will reduce the grade for your projects. These are mostly derived from the GNU standards for writing C, which are the standards typically used in open source software.

The right tools make this requirement easier! On stu (and all Linux systems), there is a command-line utility called clang-format that will automatically format your source code according to various standards. You can run it on one file at a time by passing the file name. By default, it would just print out the formatted version, but you can redirect that output into a file (in this case, main.c.gnu) and rename it to overwrite the original version.

$ clang-format --style=gnu main.c > main.c.gnu    [format main.c file]
$ mv main.c.gnu main.c                            [rename to overwrite the original]

Banned C Functions

The functions listed below are common sources of security vulnerabilities, and their use is banned. Use of any of these functions will result in an automatic penalty to projects.

The following list of functions are banned and any use of any of them will yield a penalty on projects:

atoi(), atol(), atoll()
You must use strtol() instead.
atof()
You must use strtod() instead.
gets()
You must use fgets() instead, using stdin as the FILE* argument.
strcat(), strcpy(), sprintf()
You must use strncat(), strncpy(), or snprintf() instead. (Bonus: It would actually be better to use strlcat() or strlcpy(), but those are considered non-standard C.)

JMU Honor Code Statement

Every project submission must contain the following section which follows the class description or must cite any sources used (such as a TA). This statement should appear in the specified location of the src/main.c file. Failure to include this statement will result in a 10 point penalty.

/* This work complies with the JMU Honor Code.
 * References and Acknowledgments: I received no outside
 * help with this programming assignment. */
OR

/* This work complies with the JMU Honor Code.
 * References and Acknowledgments: TA Ringo helped me
 * with the sgt_pepper() function. */

Coding Style Requirements

  • Comments - Use them. Generally speaking, every 5-10 lines of code should have a brief one-line comment like shown below.
  • Consistent file structure - All .c and .h files should look like this:
    /* zoo.h */
    #ifndef __CS361_ZOO__
    #define __CS361_ZOO__
    
    /* other headers if needed */
    #include <stdbool.h>      /* bool data types */
    
    /* struct declarations needed by code other than zoo.c */
    typedef struct zoo {
      /* zoo's internal stuff */
    } animal_t;
    
    bool init_zoo (animal_t);
    #endif
    /* main.c */
    #include <stdio.h>      /* Standard I/O */
    /* other includes here */
    
    /* struct declarations needed only by code in THIS file */
    typedef struct foo {
      /* foo's internal stuff */
    } my_foo_struct_t;
    
    /* prototypes for functions needed only by code in THIS file */
    void print_foo (my_foo_struct_t *);
    
    /* global variables */
    my_foo_struct_t master_foo;
    
    /* function definitions */
    int
    main (int argc, char **argv)
    {
      print_foo (&master_foo);
      return 0;
    }

  • Indentation and alignment - Two spaces with curly braces on the next line. Examples:
    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 lower-case and use an underscore ('_') to separate words. Pick meaningful names and types.
    Do this: Don't do this
    size_t bytes_read;
    char *message_read;
    int b;
    char *MyIncomingMessage;

  • No 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)
    {
      /* ... */
    }

  • No long functions - Restrict functions to two screens (about 100 lines of code), and never have more than 3 levels of indentation. Code that is longer than this or uses more levels of indentation is typically too complex to maintain. For instance, you should never do this:
    Don't do this
    if (...)
      {
        while (...)
          {
            if (...)
              {
                if (...)
                  {
                    for (...)
                      {

  • Safe pointer practices - 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.
  • Use assertions for invariants - If there is a condition that should never be false (i.e., very bad, unpredictable behavior will occur), include an assert() in your code in an appropriate place. (Don't forget to #include <assert.h>) For instance, assume that a function takes an integer parameter input and the input should only be between 1 and 5. At the top of your function, you should have a statement like this:
    Do this
    assert (input > 1 && input <= 5);
    if (input == 0)
      cause_my_computer_to_blow_up_while_playing_one_direction_songs ();

  • Always initialize local variables - If you do not provide an explicit initial value for local variables, the variable will be created with an unpredictable, random value. This could cause very 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 tend to be good options.

Recommended guidelines

In addition to the requirements above, the following practices are used in the base source code distributions. Following these guidelines would help your code to match the existing style and make your code more readable.

  • When making a function call, put a space between the function name and the opening '(' as well as one after each comma:
    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);

  • When using malloc() to dynamically allocate an array, use sizeof() instead of making assumptions about data sizes:
    Do this: Don't do this
    size_t num_entries = 5;
    uint16_t *data =
        malloc (sizeof (uin16_t) * num_entries);
    uint16_t *data = malloc (10);

  • Short-circuit functions when checking for errors rather than using if-else blocks.
    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;
      }

  • Avoid global variables. Global variables should be thought of as a last resort, as the values can change from one function call to the next.
  • Use #define for constants as appropriate. The name of the constant should be in all upper-case:
    Do this: Don't do this
    #define NUMBER_OF_QUESTIONS 5
    for (question = 0; question < NUMBER_OF_QUESTIONS;
         question++)
      {
        /* stuff here */
      }
    for (i = 0; i < 5; i++)
      {
        /* stuff here */
      }



James Madison University logo


© 2011-2024 Michael S. Kirkpatrick.
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.