Coding Style – C

Coding Style is a set of guidelines for a specific programming language that recommend programming style, practices, and methods for each aspect of a program written in that language. These conventions usually cover file organization, indentation, comments, declarations, statements, white space, naming conventions, programming practices, programming principles, programming rules of thumb, architectural best practices, etc. These are guidelines for software structural quality. Software programmers are highly recommended to follow these guidelines to help improve the readability of their source code and make software maintenance easier. Coding conventions are only applicable to the human maintainers and peer reviewers of a software project. Conventions may be formalized in a documented set of rules that an entire team or company follows, or may be as informal as the habitual coding practices of an individual. Coding conventions are not enforced by compilers [ 1 ].

Some examples of Coding style guidelines for C:

  1. R. Heinsar Coding Style [ 2 ]
  2. Linux kernel coding style [ 3 ]
  3. C Coding standard [ 4 ]

This style guide is derived from the fore mentioned example guidelines and others.

NB! It is important to follow the guidelines in order to write readable code. One should not mix different styles within one project e.g. naming variables both in camel case and underscore style.

Table of Contents

Naming

As a general rule of thumb variables should have meaningful names. This improves code readability and reduces the necessity of documentation and comments. Try to avoid cryptic and one letter variable names if possible.

There are 2 major naming conventions regarding multi word variable names:

  1. Camel case [ 5sampleVariableName
  2. Underscore [ 6sample_variable_name

In this course the underscore capitalization is preferred for naming variables.

Variables

Declaring variables

char *job_title = "Software Engineer";
char name[32] = {0};
char courses[10][32] = {0};
unsigned int age_years = 0,
             height_cm = 0,
             iq = 0;
float average = 0.0;
void *memory_start_addr = NULL;
  • Variable names should be descriptive
    • When data has units, add it as a suffix to variable name
      • e.g. milliseconds:  _ms
    • When dealing with arrays make variable name plural
      • e.g. array of names:  names
  • Abbreviate variable names if possible
    • e.g. address: addr
    • e.g. counter: cnt
  • Always try to initialize variables at declaration to avoid problems
    • e.g. when declaring string the last element is not guaranteed to be null terminated
  • Group declaration of same type variables, separate declarations with commas
  • Avoid global variables if possible
  • On pointer declaration asterisk goes before variable name without space
  • Think about the the type, memory usage  and signedness of values for the data
    • e.g. Age cannot be negative, if we used it for human age then unsigned char would suffice. Since it has only positive numbers and the highest value would be 255.
    • e.g. Average should be real number
// Bad example
char* job_Title = "Engineer", n[32]; /* mixed capitalization, non descriptive names
                                        asterisk should not be next to datatype */
unsigned int average; // Possible to store only integer values
char Exit; // first character should not be capitalized
float a; // What kind of data will be stored here?
char arr[10][32]; // What kind of data will be stored here?

Type definitions and structures

Try to avoid type definitions, however when used don’t add reserved suffixes and prefixes. The reason for this is that some libraries might already have defined a type with same name. Those prefixes include ‘_’ and ‘__’ and as a suffix ‘_t‘.

To improve readability of code use structs instead of typedefs.

When defining structures describe the purpose of member elements.

struct binary_tree_node {
    int value;                      /* Value stored in node */
    struct binary_tree_node *left;  /* Left child */
    struct binary_tree_node *right; /* Right child */
};

typedef binary_tree_node node_t;

// Node of binary tree
struct binary_tree_node root;
// What kind of node is this?
node_t root;

Preprocessor directives

#define

Defines should all be named all uppercase and words separated by underscores.

#define MAX_ENTRIES 10
#define BITN(N)(1 << (N))
#define INFO(VAR)                                       \
do {                                                    \
    printf("Line %d: %4d - %8x\n", __LINE__, (X), (X)); \
} while (0)
  • Multiline  macros should be wrapped around do while loop
  • Definitions are mainly used to remove magic numbers from code
    • Doing so improves readability and simplifies refactoring

Functions

int sum(int *array, unsigned int size);
  • Functions should perform one specific task
  • Name should describe the task function is solving
  • Functions which are non void must always return a value
  • Functions must make use of all parameters
    • If function should take no parameters it should have void in parenthesis
  • Function should have prototype before main function

Formatting

Braces

The indentation style must be followed throughout the project. There are 2 [ 7 ] main ways of writing braces your code:

  • K&R style ( Preferred )
    while (x < 10) {
        calculate(x);
        x++;
    }
  • Allman ( Accepted )
    while (x < 10)
    {
        calculate(x);
        x++;
    }

K&R style:

  • Space before and after parentheses
  • Opening brace ‘{‘ at the end of line and matching ending brace ‘}’ on a new line after code block
  • Code block between braces must be indented
  • Always have braces, even if there is only one statement in the code block

Parenthesis ()

if (i == 25) {
    // ...
}

while (i < 100) {
    // ...
}

void my_function(int *array);

my_function(numbers);
  • Put spaces between keywords
    • if, while, for, etc..
  • Don’t put spaces between functions
    • prototypes, definitions, calls

Conditionals

if (x < 10) {
    calculate(x);
} else if (x < 100) {
    calculate(x - 10);
} else {
    x = 1000;
}

Switch

switch (key) {
    case 'w':
        forward();
        break;
    case 'a':
        left();
        break;
    case 's':
        backward();
        break;
    case 'd':
        right();
        break;
    default:
        wait();
}
  • Switch content is indented
  • Case content is indented
  • Switch should always have default action
  • Be aware of break keywords, if they are missing cases are fallthrough

Indenting

int main(void) {

    int state = 0;
    char input = 0;
    
    while (1) {
        switch (state) {
            case 0:
                if (input == 'e') {
                    exit(0);
                } else {
                    printf("Unknown command!\n");
                }
                break;
            case 1:
                for (int i = 0; i < 25; i++) {
                    printf("%d\n", i);
                }
                break;
            case 2:
            case 3:
            case 4:
                printf("Do the same things for 2,3,4\n");
                if(input == 'a') {
                    if (state == 3) {
                        printf("You made it quite far");
                    } else {
                        state = 10;
                    }
                }
                break;
            default:
                wait();
        }
        state = ((state + 1) % 5);
        scanf("%d", &input);
    }

    return 0;
}
  • Indentation is additional space before the statement or expression.
    • In this style guide this is 1 tab (4 spaces)
  • Indenting your code improves readability
    • Code blocks become easily distinguishable
  • Code block between curly braces should be indented

One statement per line

// Good
int grade = 0,      /* desc. */
    grades[5] = {0},/* desc. */
    student_id = 0; /* desc. */
// Good
int grade = 0;      /* desc. */
int grades[5] = {0};/* desc. */
int student_id = 0; /* desc. */

// Bad
int grade = 0, grades[5], student_id = 0;
  • One statement per line helps improve readability
  • Possible to add documentation / comments about variables on the same line
  • Cleaner code and easily seen if the variable is initialized

Spaces

Use spaces after the following keywords:

if, else, else if, switch, case, for, do, while

Don’t add spaces inside the parenthesized expressions. Bad example:

s = sizeof( int );

When declaring a pointer * is adjacent to variable / function name not data type

int *numbers;
int *get_numbers(int *amount);

Use spaces around the following operators:

= + - * / % < > | & ^ <= >= == != ? :

Don’t use space after the unary operators:

& * + - ~ ! sizeof

Don’t use space after the prefix increment / decrement operators:

++ --

Don’t use space before the postfix increment / decrement operators:

++ --

Don’t use space around structure member operators:

. ->

Use space after comma when separating elements, parameters etc.

int numbers[] = {1, 2, 3, 5};
function(12, names, grades);

Line length

Lines should not exceed 80 characters.

  • Improved readability
    • Lines are easier to follow
    • Cleaner code
    • Better overview at one glance
  • In case the code needs to be printed, it will be still neatly formatted
  • Easier to follow diff in terminal
  • Many text editors support the option of adding vertical line at 80 characters (Vertical ruler)

Splitting long lines

time_t n = time(NULL);
struct tm *now = localtime(&n);

printf("Now: %02d-%02d-%04d %02d:%02d:%02d\n", now->tm_mday,
        now->tm_mon, (now->tm_year + 1900), now->tm_hour,
        now->tm_min, now->tm_sec);
printf("Really Long line "
        "Ends here "
        "On this line %d\n", 3);
  • When possible split between parameters
    • Indent at least until the open parens
  • When splitting strings, end the string and start a new string from new line (2. printf)

Documenting

Consider your comments a story describing the system. Expect your comments to be extracted by a robot and formed into a man page. Class comments are one part of the story, method signature comments are another part of the story, method arguments another part, and method implementation yet another part. All these parts should weave together and inform someone else at another point of time just exactly what you did and why.

Comments should document decisions. At every point where you had a choice of what to do place a comment describing which choice you made and why. Archeologists will find this the most useful information [ 4 ]. Example of a comment which is not very useful[ Image ].

// This is a single line comment
function(params);

function2(params2); // This is an inline comment

/*
 * This is multi line comment
 * Which will describe something
 * in a more detailed way.
 */

int student_id = 0;         /* Matricular number */
char *student_name = NULL;  /* Students name */
  • Follow the maximum suggested line length practice (80 characters)
    • when necessary split lines and use multiline comments
  • Space after the comment symbols

Functions

/*
 * Description: Multiplies unsigned number by 2.
 * 
 * Parameters:  number - number to be multiplied.
 * 
 * Return:      Returns multiplied number.
 */
unsigned int multiply_by_two(unsigned int number) {
    return number << 1;
}
  • Briefly describe the function
    • If there are some nuances to the usage of this function, express them
  • Describe the parameters and its purpose
  • What will the function return

Miscellaneous

Magic numbers

No magic numbers!  Instead use #define directives to define constants. This will benefit you in many ways:

  • Improved readability
    • Understanding about what a certain number does – the meaning
    • Better comprehension of the code
  • Faster refactoring
    • Modifying define at the end of the file vs in 100 places all over the code
  • Less prone to errors
    • There may be many magical numbers which coincidentally are the same. If at one point refactor is in due, one might change too many values thus possibility of breaking the code

Functions

  • Create function prototypes before main function and definitions after main function
    • When writing your own library write prototypes to .h files and definitions to .c files
  • Make use of the function return value
    • If function does not return a value, declare it as void
  • Make use of all function parameters
    • If a parameter is not used inside a function, remove it
  • Function should fulfill one purpose
    • Instead of solving the whole problem in one function, divide it into sub-problems and functions

Files

/**
 * File:        style_guide.c  
 * Author:      CodingMeta
 * Created:     07.10.2017
 * Last edit:   15.10.2017
 * 
 * Description: Code snippets for style guidelines.
 */
  • Add comments to .c and .h file headers
    • File name
    • Author
    • Created
    • Last edit
    • Description
    • License (optional) – See most common open source licenses [ 8 ]
  • Gives an overview about the contents of the file and the ownership

Resources

  1. Coding conventions [ Link ]
  2. R. Heinsar Coding Style [ Link ]
  3. Linux kernel coding style [ Link ]
  4. C Coding Standard [ Link ]
  5. Camel Case [ Link ]
  6. Underscore [ Link ]
  7. Indentation Style [ Link ]
  8. Choose a License [ Link ]

5 thoughts on “Coding Style – C

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.