C Programming Conventions
This document is based on the coding conventions of Carnegie Mellon Institute of Technology, itself based on the NetBSD conventions and the C++ conventions of Todd Hoff. A configuration file for clang-format is available at the end to best respect these conventions.
This document lists recommendations for writing code in C (in particular for the Structured Programming and Advanced Programming modules). Of course these recommendations are valid regardless of the language used for coding (French/English). In the rest of the text, examples will be given in English.
Names
In general, it is important that names (of variables/functions/files) are explicit so that the reader has an idea of what is being manipulated. Therefore, avoid (except in very common cases such as i for a loop variable or x for an unknown) overly generic names.
Function Names
Functions (and procedures from an algorithmic point of view) represent a sequence of instructions that achieves an objective. It is generally common to name functions with a verb (for example: compute, print, get…). To make function names more explicit, multiple words in lowercase separated by _ will be used.
Example:
print_array,compute_max, …
Some function prefixes are frequently used, for example is_ when expecting a boolean answer, get_ to obtain a value, or set_ to define a value.
Example:
is_array_empty,get_maximum,set_state…
Variable Names
Variables (and even types) represent things. It is common to name them with nouns (for example: maximum, index, speed…). The naming convention will be the same as for functions, i.e., lowercase with words separated by a _ character (except for constants which will be in uppercase).
Example:
error_number,iteration_limit,average_value…
In the case of variables representing physical quantities, variables will be suffixed with their unit.
Example:
timeout_msecs,length_m…
For pointers, variable names will be prefixed with p_:
Example:
int *p_maximum
For global variables, the prefix g_ will be used (allows identifying the scope of the variable by its name):
Example:
char * g_working_directory
Structure or Type Names
The following convention will be used: first letter of each word in uppercase (which serves as a separator), the rest in lowercase. For structures, care should also be taken to organize variables to minimize waste related to memory alignment. Example:
typedef struct
{
unsigned char id;
unsigned char birth_date;
short int phone_number;
char first_name[MAX_LENGTH];
char last_name[MAX_LENGTH];
} Person;
typedef Person* PtrPerson;
Variables
Pointers
For pointers, the asterisk * must be placed close to the variable and not the type to avoid confusion when multiple variables are declared on the same line. For example:
char* name, letter;might suggest that both variablesnameandletterare pointers to characters, whereas onlynameis. The syntaxchar *name, letter;is more explicit and faster to parse.
Global Variables
Global variables should be avoided as much as possible. If in certain cases their use is unavoidable, care should be taken to respect the naming convention (i.e., prefix g_).
Constants
The use of the keyword const is preferred over #define since const provides type information, which allows the compiler to check type compatibility when using the constant. Constants should also be in uppercase with words separated by _. For example:
const int BUFFER_SIZE = 512;
Macros
For macros, the same naming convention as for constants is used (uppercase + _). Be generous with parentheses to avoid undesirable side effects. For example:
#define SUM(a,b) ( (a) + (b) )
Enumerations
For the enumeration name, the type convention will be used (uppercase on the first letter of each word, the rest in lowercase). For each enumeration label, the constant convention will be used (uppercase with _ as separator).
enum GeometricSimplex
{
VERTEX,
LINE,
TRIANGLE,
TETRAHERDON
} ;
Blocks
Brace Placement
Either the K&R convention (for Kernighan & Richie) can be used, which places the opening brace on the same line as the condition:
if ( condition ) {
instruction_1;
instruction_2;
instruction_3;
}
or the Allman convention (or ANSI C) which places the opening brace on the next line:
if ( condition )
{
instruction_1;
instruction_2;
instruction_3;
}
Choose whichever you prefer and be consistent throughout all your programs! In all cases, the instructions in the block must be indented one tab to the right.
Use of Braces
In general, it is preferable to always use braces even when there is only one instruction in the block. This avoids errors if the code is modified and an instruction needs to be added to the block:
if ( condition )
instruction_1;
instruction_2;
always executes instruction_2, whereas
if ( condition )
{
instruction_1;
instruction_2;
}
only executes instruction_2 if the condition is true. To maintain code compactness, sometimes the block can be written on one line when there is only one instruction. For example:
if (condition) instruction_1;
Comments After Closing Braces
For cases where your blocks are long, it may be useful to add comments at the end of closing braces to better navigate your program. For example:
while ( condition_1 )
{
if ( condition_2 )
{
// ...
} /* condition_2 OK */
else
{
// ...
} /*condition_2 NOK */
} /* loop until condition_1 NOK */
Conditions
Try as much as possible to put the constant on the left side of the comparison operator in conditions (these are called yoda conditions). This allows the compiler to detect the confusion between the equality operator == and the assignment operator =. For example:
if ( BUFFER_SIZE == size )
And avoid as much as possible implying comparisons to 0 or NULL. For example:
if ( NULL == ptr_index)orif ( READ_ERROR == func(filename) )
is preferable to
if ( ptr_index )orif ( func(filename) )
The fact that tests are explicit makes it easier to detect errors.
Abbreviated Form of if-then-else
It is possible to use the abbreviated form of if-then-else; however, care should be taken to make it clear by avoiding a then or else that is too long… the use of functions is therefore recommended. For example:
( condition ) ? func_1() : func_2()
goto, continue, break
Avoid using them as much as possible since their use produces programs whose execution is difficult to trace. In general, the same result can be obtained with a better-structured program.
Formatting
Parentheses
To easily distinguish functions from keywords, the following convention is used:
- The opening parenthesis is inserted immediately after the function as:
max(a, b); - A space is inserted between the opening parenthesis and the keyword as:
while (condition) - Avoid parentheses for
returnwhen they are not necessary
One Variable/Instruction per Line
The idea is to facilitate code readability by avoiding multiplying variable declarations or multiple instructions on the same line. Thus avoid:
int *size, i, **matrix;
i = 0; load_matrix(matrix, size);
and instead write… and especially always initialize variables:
int * size = NULL;
int i = 0;
int **matrix = NULL;
load_matrix (matrix, size);
Assignment in an Expression
In the same spirit as the previous paragraph and with the goal of readability and code maintenance, avoid assignments in an expression. For example:
d = (a = b + c) + r;
will be written as:
a = b + c;
d = a + r;
Miscellaneous
No Magic Numbers
Avoid putting values in programs that have no meaning. Use either variables or constants to clarify the meaning of each value. This allows for code evolvability and readability.
For example in the following code:
int i, j;
for ( i=0 ; i < 1000; i++)
{
for ( j=0 ; j < 1000; j++ )
{
// ...
}
}
it is impossible to know whether the 1000 in the first for corresponds to the same quantity as the one in the second for. So if the value of the stop condition of the first for needs to be changed, should it also be changed for the second for? In the current state it is impossible to know without carefully reading the code. With the following version, understanding the code and therefore its maintenance is much easier:
const int VECTOR_SIZE = 1000;
const int MAX_ITERATION = 1000;
int i, j;
for ( i=0 ; i < MAX_ITERATION; i++)
{
for ( j=0 ; j < VECTOR_SIZE; j++ )
{
// ...
}
}
Return Codes / System Calls
Except in special cases (for example for printf), test the return codes of system calls (fopen, malloc, realloc…) to better trace program execution and detect possible bugs or errors.
Using #if
If you want to have code that is only compiled under certain conditions, it is possible to use the pair #if COND and #end. If COND equals 1, the code will be compiled; otherwise it will be ignored by the compiler. Consider the following program:
#include <stdio.h>
int main()
{
#if DEBUG
printf("DEBUG set to 1\n");
#end
return 0;
}
Depending on how it is compiled, a different result is obtained:
jdequidt@weppes:~$ clang exemple_if.c -o exemple_if -Wall
jdequidt@weppes:~$ ./exemple_if
jdequidt@weppes:~$ clang exemple_if.c -o exemple_if -Wall -DDEBUG=1
jdequidt@weppes:~$ ./exemple_if
DEBUG set to 1
jdequidt@weppes:~$
Using this technique, it is very easy to comment out large sections of code using #if 0 and #end. For example:
#if 0
// code that we do not want to compile
#end