Coding style

Introduction

The main purpose of a coding style (or coding standard) is to ensure that the work of multiple contributors within a project (or a company) produces code that is uniform in style. That increases the readability of the code and makes it easier for others to update the code if/when that should become necessary. The styles vary from project to project and company to company so the first step when becoming a contributor/employee is to familiarize yourself with the coding style that the project/company uses.

A good coding style may also reduce coding errors, for example it’s easier to spot a missing brace when braces are always on a separaate line. In the end tho the code needs to be both syntactically and semantically correct and style alone doesn’t guarantee this. During compiling all style elements like indentation, multiple spaces, newlines and comments are removed.

Line length

A line should not be longer than 80 characters. This comes from historically monitors being able to display 80 characters per line.

Since technology has advanced over time, the line length limit of 80 is strongly recommended (future employer might not be so lenient). Exceeding a by one or two characters will not be frowned upon, but once you reach 90 or more, you need to wrap your lines or restructure your code.

IDEs provide visual clues as to when a line is over length.

Geany: Edit -> Preferences -> Editor -> Display
Choose Long line marker: Enabled and Column: 80

Splitting lines

C language doesn’t care about spaces or tabs. Thus, splitting lines in C doesn’t require any additional symbols. Splitting lines is partially regulated and partially an art. The goal is to make it readable.

  • Line is being split before reaching 80 characters
  • Split lines are indented by at least one.
  • Since splitting is partially a creative undertaking, it may be prudent to indent by more than 1.
  • It is in good style to split lines before or after operators
  • Once the statement is finished, the code continues as it was before.
Don’t do this:

The following line is too long and requires horizontal scrolling. It is hard to read and modify.

Splitting strings

Strings cannot be split in between the double quotes.

  • First, finish the strings using double quotes. Then start the string again on the next line.
  • It’s recommended to indent to the point where the string begun on the previous line.
  • Alternatively, you can split up the string in between multiple print statements.

Comments

The purpose of commenting code is to increase readability and manageability of the code. Everything may seem clear when initially writing the code, but when returning to it in a couple of days, weeks, months or even years, it will be very difficult to figure out what was written at the time and why is it written like that. Even worse will be to find and fix mistakes and make changes.

Well commented code isn’t only important for you, but also your coworkers who you will share the codebase with.

You should mainly comment “interesting” parts of the code. Understanding what is interesting comes with experience. Definitely do not comment every line – this increases unreadability. For starters, aim to have 1 comment every 5 lines on average. Once you gain experience, sometimes it will be necessary to comment every line, at other times, you might not see a comment for 10 – 20 lines straight.

There are several options for writing comments:

  • Single line comments
  • Inline comments
  • Multiline comments (see File Prolog)
  • It is recommended to prefer separate line comments to inline comments for readability
  • Put a space after the comment symbols.
  • Comment starts with 2 forward slashes and ends when the line ends.
  • Prefer short and straight to the point comments.
  • Adhere to line length limitation.

A good vs bad comment

A good comment explains at least one of the following:

  • what are you doing
  • why are you doing it
Don’t do this! A bad comment explains how you are doing it.
The comment is bad because it doesn’t provide anything addition to what can already be read from the code. If there’s a mistake in the code, you cannot identify it. Given this code to a friend or a colleague, they can not see the discrepancy between what you intended to do and what you actually wrote in code.

File header

All .c and .h files should have a header that lists the name of the file, author(s) and the purpose of the code in the file. For public projects, an e-mail address or a URL may also be added.

Often the creation creation and last change date (or version number) is also added to the header. In some cases you may also see the changelog of the file, however these days it is more likely to be in the version control software (e.g. Git).

Multiline comments are typically between the /* and */ symbols – everything in between is considered a comment.

Declaring and initializing variables

  • Variable names should indicate what they are for (self-explanatory).
  • Names should follow lowerCamelCase  convention: first word is lowercase, every following word starts with uppercase.
  • It’s possible to declare several variables of the same type with a single declaration, in that case variables should be separated by comma.
  • Initialization should be done only when declaring a single variable or on a separate line.
  • Avoid global variables (some exceptions apply),
  • Variables can be either declared in the start of the function (C90 standard) or when first used (C99 standard). The latter may not be supported by all compilers.
Do not do this!
  • First line: single letter variable names should be avoided, as their purpose is hard to understand later on and accidentally mixing them up is quite common. A reasonable exception to his would be naming loop counters and commonly understood names (e.g. n – number of objects)
  • Second line: It’s a mix of initialized and uninitialized variables. They should be declared on separate lines, as this is confusing and mistakes are prone to happen.
  • Third line: Even though the name seems descriptive, it is written in an unreadable manner, as the word separation is not clearly visible. It should also be considered if this long variable names are reasonable.

Declaring pointers

Declaring pointers mostly uses the same principles as declaring variables. There are a few additional detalis to notice however.

  • The symbol * is right before the variable name (no space in between).
  • Pointers are typically prefixed with a lower case p character to indicate it being a pointer.
  • Pointers are declared separate from non-pointer variables.
Do not do this!

  • Writing the asterisk after the data type gives false assumption that all variables declared on this line will be pointers.
  • In the second line you may think that val  is also a pointer, but instead it will be an integer. Always declare pointers and non-pointers separately.

Arithmetic and boolean operations

  • All operations that take 2 or more arguments (binary operators) should have spaces around the operator.
  • Unary operators should have no spaces between operator and preceding/following operand. For example !holiday  (NOT holiday), ~a  (INV a), k--  (decrement k by 1).
Do not do this!

The lack of spaces is causing issues with the readability of this statement. It will be difficult to check if it was written correctly and introduce changes.

#define preprocessor directives

  • Defines are used to avoid magic numbers in the code, amongst other things
  • Defines are in FULL_UPPERCASE. If the name consists of several words then underscore is used to separate them.
Do not do this!
  • In the first example, the macro definition may be mistaken to a variable declaration. This may cause confusion later, as you may try to change this value, however cannot.
  • The second example has bad readability as the word separation isn’t clearly visible.

Conditionals

  • Opening and closing braces should be on separate lines.
  • The curly brackets must be the same distance from the left edge (e.g. they align underneath each other)
  • Content of the braces is indented by 1 (4 spaces).
  • The keyword else  is on a separate line. There are no empty lines between the last closing curly bracket and the keyword else .
  • There is a single space between the keyword if  and ( .
  • Arithmetic, comparison and logical operators are surrounded by spaces. This does not apply to unary operators (~, ^, &, |, !)
Do not do this!
  • Placing of the curly brackets is confusing, regardless of the style you may have.
  • The empty line before the else  keyword makes it not clear that it is connected to the if  statement before. It also may tempt to write code statements in between, which would be an error.

Switch

  • There is a space after switch.
  • There is a space after case.
  • The contents of switch is between curly brackets and is indented. Curly brackets are on separate lines and they are the same distance from the left (horizontally aligned).
  • The contents of each case are indented once more.
  • Don’t forget break statements.

Indentation

Indentation is the action where we change the padding between the start of the line of code from the left edge. It is done to increase readability of the code. It provides clear understanding of the contents of a clode block (e.g. if  statement, for  loop, etc).

The indentation step in this course is 4 spaces. If you use the tab key in, it is recommended to configure your editor to replace a tab with 4 spaces. If tabs are left in, you may encounter your code looking different from one IDE to another (different editors are configured to show tab as 2, 4 or even 8 spaces).

Avoid cross-use of tabs and spaces.

The following example is a nicely indented piece of code structure.

  • The open and close curly braces are always aligned underneath each other.
  • The open and close curly brace are on their own lines.
  • The code block between the curly braces is indented by 1 (4 spaces).
  • Hint: Most IDEs allow to increase the indentation of 1 or more lines of code by hitting the tab  key. They also allow to decrease the indentation of 1 or more lines of code by hitting the shift+tab  key combination.
  • Recommendation: configure your IDE in such a way that it will automatically create a closing parenthesis after creating the opening one. If it is not possible, type the closing one immediately so you wouldn’t forget it. Our geany configuration provides automatic closing of parthesis.
  • Recommendation: try to indent the code as you type. Most IDEs indent for you as soon as you type { and hit Enter. Having it done automatically avoids mistakes.
Do not do this!

It is difficult to understand the structure of the code.

Indentation depth

Avoid nesting (indenting) more than 3 levels. This limit is also used in the industry (sometimes also 4 levels is used).

Excessive indentation makes it hard to follow the code. To combat this, code is refactored by putting parts of it in user-defined functions, combining statements (e.g. nested if statements) or relocating parts of the code.

Loops

  • Statements while  and for  are followed by space.
  • Braces are horizontally aligned (underneath each other).
  • Braces are on separate lines.
  • The contents of braces are indented.
  • In the for statement there should be a space after each semicolon.

Nested loops

When nesting loops, you must follow the general indentation guide – e.g. the contents of each code block is indented.

 Functions

main() function

või

  • Use first style when you don’t intend to pass any parameters to your program from command line (console, terminal).
  • Use the second style if you DO intend to pass additional parameters (like filenames, switches, etc).

User-defined functions

  • The function name should explain the purpose of the function.
  • All words in the name are concatenated.
  • UpperCamelCase  convention is used for naming the functions. This makes functions distinctive from variables that use lowerCamelCase .
  • Each function should have a prototype either before the main() function or in the header file.

Function comments

A multiline comment should precede the function explaining what it does. The comment usually also details the arguments and return value and any possible side-effects the function may have (if any).

Standard comment
  • Description – states the purpose, limitations and the workings of the function. Any data altered that’s not local to the function must also be specified (e.g. arrays).
  • Parameters – their names and semantics (what are they used as in the function). Any requirements for the values passed should also be specified!
  • Return value – data type and semantics
Shortened form

Shortened form should only be used for very basic functions.

Function prototypes

  • You should make a prototype for each of your functions.
  • Naming convention is the same as for functions.
  • Both type and name should be listed for parameters.
  • Prototypes of your functions should be listed in a separate header file (recommended) or in the .c file after the preprocessor directives (#include, #define).

Enumerations

Names of the enumeration types should be UpperCamelCase  and the identifiers should be in SCREAMING_SNAKE_CASE .

Use either a single-line definition style

or a multiline definition style.

Note: in general, avoid using typedef  to redefine the name of the enum.

Structures

For structures, we use the same agreed upon rules for indentation and placement of curly braces.

Note! Even though using typedef  is considered hiding information, in the case of structures and unions it is acceptable within this course.