Conseils de codage en C/Facilité de modification

Un livre de Wikilivres.

Un programme est souvent amené à être modifié, adapté, réutilisé par celui qui l'a écrit ou par un autre, sur une longue durée (correction d'erreur, évolution du logiciel, réutilisation dans une autre application...).

Ces conseils visent à réduire les efforts nécessaires lors d'évolutions.

Éviter les constantes littérale (c_mod_1)[modifier | modifier le wikicode]

Les constantes chaîne, numérique doivent être représentées par des symboles. Les constantes littérales sont interdites.

Justification[modifier | modifier le wikicode]

Les constantes littérales dupliquées et réparties dans le code source rendent les modifications plus difficiles : risque d'altération, d'oubli d'une occurrence. Il faut les regrouper sous formes de constantes symboliques (définition du pré-processeurs) en tête de fichier source ou dans des fichiers inclus.

Exemple[modifier | modifier le wikicode]

  • Mauvais : perimetre = 3.14159 * diametre;
  • Meilleur : #define PI 3.14159 ... perimetre = PI * diametre;

Écrire des commentaires fonctionnels (c_mod_2)[modifier | modifier le wikicode]

Les commentaires ne doivent pas paraphraser le code, ils doivent expliquer la logique de traitement et justifier les choix effectués (pourquoi tel algorithme, telle structure de données, ...).

Justification[modifier | modifier le wikicode]

La personne, qui relira le code source en vue de sa maintenance, aura besoin de comprendre les grandes étapes de l'algorithme, les fonctions réalisées par la suite d'instructions du langage.

Exemple[modifier | modifier le wikicode]

Mauvais :

// Incrémentation de i
i = i + 1

Meilleur :

// Passage au fichier suivant
i = i + 1

Pas d'éléments inutiles ou non initialisés (c_mod_3)[modifier | modifier le wikicode]

Toute fonction définie doit être utilisée et toute variable déclarée doit être initialisée.

Justification[modifier | modifier le wikicode]

Évite la présence de code mort qui nuit à la maintenance.

La valeur par défaut d’une variable dépend de facteurs qui peuvent échapper au programmeur. Cette règle évite les fonctionnements aléatoires de certains programmes.

Outils[modifier | modifier le wikicode]

Les compilateurs et les outils de vérifications statiques comme splint émettent des avertissements en cas de problème. Certains compilateurs possèdent des options pour générer tous les avertissements sous forme d'erreur, ce qui permet d'interrompre une chaîne de compilation afin de corriger le code.

Pas de duplication de code source (c_mod_4)[modifier | modifier le wikicode]

Ne pas dupliquer de grandes parties de code dans plusieurs fichiers source.

Justification[modifier | modifier le wikicode]

  • La duplication de source oblige à intervenir à plusieurs endroits pour corriger une erreur.
  • Rend le code source moins maintenable.
  • Lire plusieurs fois le même code ralentit sa compréhension.

Les parties dupliquées doivent être fusionnées en une seule. Le code commun, éventuellement paramétré, deviendra une fonction. Si des contraintes de performance l'impose, l'inlining pourra être utilisé.

Outils[modifier | modifier le wikicode]

La recherche des parties dupliquées pourra se faire avec un outil de recherche de chaînes de caractères : sous UNIX grep ou à l'aide d'un éditeur s'il permet la recherche de texte dans plusieurs fichiers.

Limiter le nombre des variables externes ou globales (c_mod_5)[modifier | modifier le wikicode]

L'utilisation de variables externes ou globales doit être limité au maximum.

Justification[modifier | modifier le wikicode]

Les variables globales peuvent être modifiées par plusieurs unités de compilation, le contrôle des modifications est donc très délicat, les effets de bord sont possibles (modifications non désirées d'une variable ou d'une zone mémoire par du code lié). Un trop grand nombre de variables externes dénote une mauvaise analyse des interfaces entre unités de compilation ou une mauvaise gestion de la portée des variables.

Le mot-clé static en C permet, entre autres, de limiter la portées d'une variable globale ou d'une fonction à son seul fichier source. Il garantit que ces fonctions ou variables ne seront pas utilisées, par erreur, par d’autres unités. Les fonctions locales pourraient être masquées ou masquer d’autres fonctions de même nom, mais définies ailleurs dans le projet ou dans une bibliothèque liée. Le comportement du programme pourrait alors dépendre de l’ordre dans lequel les objets et bibliothèques sont présentés à l’édition de lien. Le respect de cette règle allégera l’édition de lien et évitera certains effets de bord.

Outils[modifier | modifier le wikicode]

  • Recherche dans les fichiers sources des mots clés extern, des déclarations de variables hors des fonctions.
  • L'utilitaire Unix nm et ses options -g et -o permet de connaître les symboles externes dans les résultats de compilation (modules objets .o, bibliothèques .a, .so).

Ne pas réinventer la roue (c_mod_6)[modifier | modifier le wikicode]

Utiliser les fonctions et bibliothèques écrites par d'autres programmeurs, principalement dans :

  • les bibliothèques standards du langage.
  • les bibliothèques de calcul numérique reconnues (LAPACK, LINPACK, BLAS).
  • tout autre développement suffisamment fiable.

Justification[modifier | modifier le wikicode]

Le respect de ce conseil limite le nombre de lignes de code à maintenir ainsi que les efforts nécessaires aux tests du logiciel.

Limiter le nombre de sortie (c_mod_7)[modifier | modifier le wikicode]

Une fonction ne devrait comporter qu'un seul point de sortie.

Justification[modifier | modifier le wikicode]

Diminue la complexité du code et facilite l'analyse du déroulement du programme en mise au point.

Contrôle[modifier | modifier le wikicode]

Rechercher des instructions return et exit à l'aide d'un utilitaire comme grep.

Évitez le type union (c_mod_8)[modifier | modifier le wikicode]

L’utilisation du type union est déconseillé, sauf pour manipuler des champs de bits.

Justification[modifier | modifier le wikicode]

Améliore la portabilité.

Pas de code dans les .h (c_mod_9)[modifier | modifier le wikicode]

Ne jamais écrire de fonctions ou d'instructions dans les fichiers d’entête .h (excepté les macros instructions).

Justification[modifier | modifier le wikicode]

Écrire le corps de fonction dans un fichier .h et l’inclure dans plusieurs sources dupliquerait ce code et pourrait provoquer des erreurs due aux symboles définis plusieurs fois. Plutôt que d’utiliser le pré-processeur, il faut répartir le code source dans différents fichiers .c qui seront compilés séparément et réunit lors de l’édition de lien.

Seuils pour les métriques déterminant la facilité de modification (c_mod_10)[modifier | modifier le wikicode]

Les métriques suivantes influent sur la facilité de modification d'un programme (ISO/IEC 9126) :

  • AVGS : Taille moyenne des instructions, calculée à partir du nombre d'opérateurs et d'opérandes distincts.
  • LEVL : Nombre maximal d'imbrications des structures de contrôle (for, while, if...) de la fonction plus un.
  • VOCF : Fréquence d'utilisation du vocabulaire dans un composant, défini par la formule : F_VOC = (N1+N2)/ (n1+n2) avec :
    • N1 est le nombre d'occurrence des opérateurs,
    • N2 est le nombre d'occurrence des opérandes,
    • n1 est le nombre opérateurs distincts,
    • n2 est le nombre opérandes distincts.
  • CALL : Nombre d’appels distincts : Nombre total d'appels de fonctions effectués dans la fonction analysée.

Seuils pour les métriques :

  • AVGS : de 2 à 10
  • LEVL : de 1 à 5
  • VOCF : de 1 à 4
  • CALL : de 0 à 7

Justification[modifier | modifier le wikicode]

  • AVGS : Les instructions trop longues sont difficiles à modifier. Elles peuvent provoquer des problèmes de priorité des opérateurs, des conversions de type involontaires.
  • LEVL : Un trop grand nombre d'imbrication de structures de contrôle pourra être une source d'erreur en cas de modification d'un programme. Il faut déplacer certains niveaux dans une fonction.
  • VOCF : Un vocabulaire qui se répète trop pourra être dû à des copiés-collés abusifs de parties de code ou à un programmeur à jeu d'instruction réduit. Les modifications à apporter devront alors s'appliquer à de nombreux endroits. Une solution consiste à factoriser le code à l'aide de fonction paramétrées ou à réutiliser du code externe développé et validé (bibliothèques spécifiques).
  • CALL : Une valeur trop élevée montre une mauvaise hiérarchisation dans la fonction. Un nombre élevé de fonctions appelée et de paramètres augmente le nombre de combinaisons possibles dans le déroulement du programme. Il faut limiter le nombre d'appel au besoin en découpant la fonction.