Ce document liste les recommandations pour l’écriture de code en C
(en particulier pour les modules de Programmation Structurée et Programmation Avancée). Bien entendu ces recommandations sont valides quelle que soit la langue utilisée pour coder (français / anglais). Dans la suite du texte, les exemples seront donnés en anglais.
De manière générale, il est important que les noms (de variables / de fonctions / de fichiers) soient explicites pour que le lecteur ait une idée de ce qui est manipulé. Ainsi on évitera (hors cas très courants comme i
pour une variable de boucle ou x
une inconnue) des noms trop génériques.
Les fonctions (et actions d’un point de vue algorithmique) représentent une séquence d’instructions qui réalise un objectif. Il est en général courant de nommer les fonctions par un verbe (par exemple: compute
, print
, get
…). Pour que les noms de fonctions soient plus explicites, on utilisera plusieurs mots en minuscules et séparés par des _
.
Exemple:
print_array
,compute_max
, …
Certains préfixes de fonctions sont fréquemment utilisés par exemple is_
lorsque l’on attend une réponse booléenne, get_
pour obtenir une valeur ou set_
pour définir une valeur.
Exemple:
is_array_empty
,get_maximum
,set_state
…
Les variables (et même les types) représentes des choses. Il est courant de les nommer avec des noms (par exemple: maximum
, index
, speed
…). La convention de hommage sera la même que pour les fonctions, i.e. en minuscules et les mots séparés par un caractère _
(sauf pour les constantes qui seront en majuscules).
Exemple:
error_number
,iteration_limit
,average_value
…
Dans le cas de variables qui représentent des grandeurs physiques on suffixera les variables par leur unité.
Exemple:
timeout_msecs
,length_m
…
Pour les pointeurs, on préfixera les noms des variables par p_
:
Exemple:
int *p_maximum
Pour les variables globales, on utiliser la préfixe g_
(permet d’identifier la portée de la variable avec son nom):
Exemple:
char * g_working_directory
On utilisera la convention suivante: première lettre de chaque mot en majuscule (qui fait office de séparateur) le reste en minuscule. Pour les structures, on pourra également prendre soin à organiser les variables de manière à minimiser le gaspillage lié à l’alignement mémoire. Exemple:
Pour les pointeurs, il faut placer l’étoile *
proche de la variable et non du type pour éviter les confusions quand plusieurs variables sont déclarées sur une même ligne. Par exemple:
char* name, letter;
peut laisser supposer que les deux variablesname
etletter
sont des pointeurs sur des caractères alors que seulname
l’est. La syntaxechar *name, letter;
est plus explicite et plus rapide à décrypter.
On évitera au maximum les variables globales. Si dans certain cas, leur usage est inévitable, on prendra soin de respecter la convention de hommage (i.e. préfixe g_
).
On privilégie l’utilisation du mot-clé const
au lieu de #define
puisque const
permet d’avoir une information de type, ce qui permet au compilateur de vérifier la compatibilité de types lors de l’utilisation de la constante. On s’assurera également que les constantes soient en majuscules et que les mots de la constante soient séparés par _
. Par exemple:
Pour les macros, on utilise la même convention de nommage que pour les constantes (majuscules + _
). On prendra soin d’abuser des parenthèses pour éviter les effets indésirables. Par exemple:
Pour le nom de l’énumération on utilisera la convention pour les types (majuscule sur la première lettre de chaque mot, le reste en minuscule). Pour chaque label de l’énumération, on utilisera la convention des constantes (majuscules avec _
en séparateur).
On peut utiliser soit la convention K&R (pour Kernighan & Richie) qui place l’accolade ouvrante sur la même ligne que la condition:
soit la convention Allman (ou ANSI C
) qui positionne l’accolade ouvrante à la ligne suivante:
Choisissez celle qui vous convient le mieux et restez consistant sur tout vos programmes ! Dans tous les cas les instructions du bloc doivent être décalées d’une tabulation vers la droite.
De manière générale, il est préférable de tout le temps utiliser les accolades même quand il y a une seule instruction dans le bloc. Ceci permet d’éviter les erreurs si le code est modifié et qu’une instruction dans le bloc doit être ajoutée cf:
exécute systématiquement instruction_2
alors que
n’exécute instruction_2
que si la condition est vérifiée. Pour maintenir la compacité du code, on peut parfois écrire le bloc sur une ligne quand il n’y a qu’une instruction. Comme par exemple:
Pour les cas où vos blocs sont longs, il peut être utile d’ajouter des commentaires à la fin des accolades fermantes pour mieux se repérer dans votre programme. Par exemple:
On essaiera autant que possible de mettre dans les conditions la constante à gauche de l’opérateur de comparaison (on appelle cela des yoda-conditions). Ceci permet au compilateur de détecter la confusion entre l’opérateur d’égalité ==
et celui d’affectation =
. Par exemple:
if ( BUFFER_SIZE == size )
Et on évitera autant que possible d’impliciter les comparaisons à 0 ou NULL. Par exemple
if ( NULL == ptr_index)
ouif ( READ_ERROR == func(filename) )
est préférable à
if ( ptr_index )
ouif ( func(filename) )
Le fait que les tests soient explicites permet de plus facilement détecter des erreurs.
Il est possible d’utiliser la forme abrégée du if-then-else
, on prendra toutefois soin de la rendre clair en évitant un then
ou else
trop long… l’utilisation de fonction est donc conseillée. Par exemple
( condition ) ? func_1() : func_2()
On évitera autant que possible de les utiliser puisque leur utilisation engendre des programmes dont il est difficile de tracer l’exécution. En général, il est possible d’obtenir le même résultat avec un programme mieux structuré.
Pour facilement distinguer les fonctions des mots-clés, on utilise la convention suivante:
max(a, b);
while (condition)
return
quand elles ne sont pas nécessairesL’idée est de faciliter la lisibilité du code, en évitant de multiplier les déclarations de variables ou instructions multiples sur une même ligne. Ainsi on évitera:
et on écrira plutôt… et surtout on initialisera systématiquement les variables:
Dans le même ordre idée que le paragraphe précédent et dans un objectif de lisibilité et de maintenance du code, on évitera les affectations dans une expression. Par exemple:
d = (a = b + c) + r;
sera écrit comme étant:
a = b + c;
d = a + r;
On évitera de mettre dans les programmes des valeurs qui n’ont pas de significations. On utilisera soit des variables soit des constantes pour préciser le sens de chacune des valeurs. Ceci pour permettre l’évolutivité et la lisibilité du code.
Par exemple dans le code suivant:
il est impossible de savoir si le 1000
du premier for
correspond à la même quantité que celui du deuxième for
. Du coup, si jamais on doit changer la valeur de la condition d’arrêt du for
, faut il également le faire pour le deuxième for
. En l’état il est impossible de le savoir à moins de regarder finement le code. Avec la version suivante, la compréhension du code et donc sa maintenance est beaucoup plus facile:
Sauf exception (par exemple pour printf
), on testera le code de retour des appels systèmes (fopen
, malloc
, realloc
…) pour mieux tracer le déroulement du programme et détecter des bogues ou erreurs éventuels.
#if
Si vous voulez avoir du code qui n’est compilé que dans certains cas, il est possible d’utiliser le couple #if COND
et #end
. Si COND
vaut 1 dans ce cas le code sera compilé sinon il sera ignoré par le compilateur. Soit le programme suivant:
Suivant la manière dont il est compilé, on obtient un résultat différent:
En utilisant cette technique, il est très facile de commenter de larges morceaux de code en utilisant #if 0
et #end
. Par exemple:
.clang-format
Le fichier est directement téléchargeable ici .clang-format. Attention en l’enregistrant, n’oubliez pas le .