« Conseils de codage en C/Robustesse des programmes » : différence entre les versions

Un livre de Wikilivres.
Contenu supprimé Contenu ajouté
Thierry46 (discussion | contributions)
Utiliser le mécanisme d'assertion
Thierry46 (discussion | contributions)
Numérotation des exigences : c_rob_1 à c_rob_7
Ligne 2 : Ligne 2 :
L'application de ces conseils suivants rendent les logiciels plus robustes : plus tolérants aux fautes.
L'application de ces conseils suivants rendent les logiciels plus robustes : plus tolérants aux fautes.


== Compilation stricte et outils qualité ==
== Compilation stricte et outils qualité (c_rob_1)==
Il faut compiler en utilsant les options les plus strictes, qui demandent au compilateur d’indiquer tous les avertissements et toutes les erreurs.
Il faut compiler en utilsant les options les plus strictes, qui demandent au compilateur d’indiquer tous les avertissements et toutes les erreurs.


Ligne 17 : Ligne 17 :
*Outil de contrôle dynamique (gestion des ressources à l'exécution) comme leaks, [[w:en:IBM Rational Purify|purify]].
*Outil de contrôle dynamique (gestion des ressources à l'exécution) comme leaks, [[w:en:IBM Rational Purify|purify]].


== switch et clause default ==
== switch et clause default (c_rob_2) ==
Les instructions de choix multiple : (''switch'', ''case''...) doivent comporter une clause pour les valeurs non testées individuellement (''default'');
Les instructions de choix multiple : (''switch'', ''case''...) doivent comporter une clause pour les valeurs non testées individuellement (''default'');


Ligne 56 : Ligne 56 :
* Un comptage des mots clés des mots switch et default dans un éditeur de texte ou avec les outils '''grep''' et '''wc''' d'UNIX.
* Un comptage des mots clés des mots switch et default dans un éditeur de texte ou avec les outils '''grep''' et '''wc''' d'UNIX.


==Test des codes retours==
==Test des codes retours (c_rob_3)==
Les codes de retour des fonctions et surtout des appels systèmes doivent être testés.
Les codes de retour des fonctions et surtout des appels systèmes doivent être testés.


Ligne 85 : Ligne 85 :
Les outils de contrôle statiques comme '''splint''' émettent un warning lorsqu'un codes retour de fonction n'est pas testé ou explicitement ignoré.
Les outils de contrôle statiques comme '''splint''' émettent un warning lorsqu'un codes retour de fonction n'est pas testé ou explicitement ignoré.


== Gestion des ressources ==
== Gestion des ressources (c_rob_4)==
Toute ressource allouée (mémoire, fichier ouvert, outil de synchronisation, ...) doit obligatoirement être libérée quoi qu'il se passe, erreur ou non.
Toute ressource allouée (mémoire, fichier ouvert, outil de synchronisation, ...) doit obligatoirement être libérée quoi qu'il se passe, erreur ou non.


Ligne 119 : Ligne 119 :
Le traitement du fichier est délégué dans une fonction séparée. Celle-ci pouvant alors retourner un code d'erreur avec l'instruction <tt>return</tt> sans se soucier de fermer le fichier ouvert, vu qu'il sera fermé juste après l'appel à la fonction.
Le traitement du fichier est délégué dans une fonction séparée. Celle-ci pouvant alors retourner un code d'erreur avec l'instruction <tt>return</tt> sans se soucier de fermer le fichier ouvert, vu qu'il sera fermé juste après l'appel à la fonction.


==Contrôle des arguments==
==Contrôle des arguments (c_rob_5)==
Avant de commencer un traitement, un module doit effectuer un contrôle minimum sur les valeurs de ses arguments (pointeur NULL, valeur hors domaine de calcul, chaîne de caractère correcte).
Avant de commencer un traitement, un module doit effectuer un contrôle minimum sur les valeurs de ses arguments (pointeur NULL, valeur hors domaine de calcul, chaîne de caractère correcte).


Ligne 126 : Ligne 126 :
Évite les erreurs provenant de la poursuite du traitement avec des données d’entrée invalides.
Évite les erreurs provenant de la poursuite du traitement avec des données d’entrée invalides.


==Remontée des erreurs==
==Remontée des erreurs (c_rob_6)==
Lorsqu’il y a une erreur, le programme ou la fonction doit générer un code spécifique à l’erreur sur la sortie réservée aux erreurs.
Lorsqu’il y a une erreur, le programme ou la fonction doit générer un code spécifique à l’erreur sur la sortie réservée aux erreurs.


Ligne 134 : Ligne 134 :
Traitement efficace des retours d'erreurs qui peuvent en étant considérée comme des valeurs de sortie normale entraîner des catastrophes.
Traitement efficace des retours d'erreurs qui peuvent en étant considérée comme des valeurs de sortie normale entraîner des catastrophes.


==Utiliser le mécanisme d'assertion==
==Utiliser le mécanisme d'assertion (c_rob_7)==
Les assertions sont des tests à placer dans vos programmes. C'est une condition qui doit être obligatoirement vérifiée. Dans le cas contraire le programme s'arrêtera.
Les assertions sont des tests à placer dans vos programmes. C'est une condition qui doit être obligatoirement vérifiée. Dans le cas contraire le programme s'arrêtera.



Version du 20 juin 2008 à 14:34

L'application de ces conseils suivants rendent les logiciels plus robustes : plus tolérants aux fautes.

Compilation stricte et outils qualité (c_rob_1)

Il faut compiler en utilsant les options les plus strictes, qui demandent au compilateur d’indiquer tous les avertissements et toutes les erreurs.

Il faut utiliser des outils qualité qui permettent d'effectuer encore plus de contrôle.

Il faudra ensuite comprendre les messages puis corrigez les sources afin d'éliminer tous ces avertissements.

Justification

Cette pratique permet d'obtenir des logiciels plus robustes et plus portables

Outils

  • gcc avec les options -Wall -Werror -pedantic
  • Outils de contrôle statique (syntaxique) comme splint, lint, proLint...
  • Outil de contrôle dynamique (gestion des ressources à l'exécution) comme leaks, purify.

switch et clause default (c_rob_2)

Les instructions de choix multiple : (switch, case...) doivent comporter une clause pour les valeurs non testées individuellement (default);

Justification

Permet de traiter les cas non prévus explicitement en détectant les cas dégradés dus à des valeurs inattendues.

Ces valeurs peuvent provenir :

  • de modifications non contrôlées d’une autre partie du code.
  • d'un mauvais interfaçage de la fonction.
  • d'une valeur non prévue reçue ou lue
  • pour un appel système, à la compilation et de l'exécution sur un système non prévu.

Exemple (extrait)

//...
  while ((optc = getopt_long (argc, argv, "htvm", longopts, (int *) 0)) != EOF)
    {
      switch (optc)
	{
	case 'v':
	  v = 1;
	  break;
	case 'h':
	  h = 1;
	  break;
	case 'm':
	  m = 1;
	  break;
	default:
	  unknown = 1;
	  break;
	}
    }

Outils

  • Les outils de vérification statique, comme splint, émettent un warning lorsqu'une clause default est oubliée.
  • Un comptage des mots clés des mots switch et default dans un éditeur de texte ou avec les outils grep et wc d'UNIX.

Test des codes retours (c_rob_3)

Les codes de retour des fonctions et surtout des appels systèmes doivent être testés.

Justification

Les codes retournés par les fonctions peuvent signaler :

  • des problèmes dans leur déroulement : validité des paramètres, erreurs de calcul...
  • des erreurs dues à l’environnement du logiciel : non - conformité de l’arborescence des fichiers, droits d’accès, ressources non disponibles...

Ignorer ces problèmes peut conduire aux pires catastrophes.

Une bonne habitude consiste à caster en void tous les appels à des fonctions dont on souhaite ignorer les codes retour. Cette pratique facilite les contrôle avec les outils qualité.

Exemple

//...
        hFile = fopen(NOM_FIC, "r");
        if (hFile == NULL)
        {
                perror("Erreur");
                (void)fprintf(stderr,
                        "Impossible d'ouvrir %s en lecture\n",
                        NOM_FIC);
                exit(EXIT_FAILURE);
        }

Outils

Les outils de contrôle statiques comme splint émettent un warning lorsqu'un codes retour de fonction n'est pas testé ou explicitement ignoré.

Gestion des ressources (c_rob_4)

Toute ressource allouée (mémoire, fichier ouvert, outil de synchronisation, ...) doit obligatoirement être libérée quoi qu'il se passe, erreur ou non.

Justification

Une ressource non libérée après utilisation peut n'être libérée qu'à la fin de l'application. Mais auparavant, celle-ci limite les ressources disponibles du système et si le code est appelé plusieurs fois, une erreur de ressource peut se produire (pas assez de mémoire, ...).

Outils

Un certain nombre d'outils permet de détecter les fuites mémoires et de voir l'occupation des ressources.

Mais avant tout, il faut avoir un code de gestion des ressources correct :

  • Tester le code de retour de la fonction d'allocation, et ne pas exécuter le reste du code en cas d'échec,
  • Libérer la ressource quoi qu'il se passe, en un seul endroit du code afin d'éviter de libérer plusieurs fois la même ressource, et si possible dans la même fonction que l'instruction d'allocation.

Exemple

//...
        hFile = fopen(NOM_FIC, "r");
        if (hFile == NULL)
        {
                perror("Erreur");
                (void)fprintf(stderr,
                        "Impossible d'ouvrir %s en lecture\n",
                        NOM_FIC);
                exit(EXIT_FAILURE);
        }
        else
        {
                erreur = traiterFichier(hFile);
                fclose(hFile);
        }

Le traitement du fichier est délégué dans une fonction séparée. Celle-ci pouvant alors retourner un code d'erreur avec l'instruction return sans se soucier de fermer le fichier ouvert, vu qu'il sera fermé juste après l'appel à la fonction.

Contrôle des arguments (c_rob_5)

Avant de commencer un traitement, un module doit effectuer un contrôle minimum sur les valeurs de ses arguments (pointeur NULL, valeur hors domaine de calcul, chaîne de caractère correcte).

Justification

Améliore la stabilité et la fiabilité du logiciel. Évite les erreurs provenant de la poursuite du traitement avec des données d’entrée invalides.

Remontée des erreurs (c_rob_6)

Lorsqu’il y a une erreur, le programme ou la fonction doit générer un code spécifique à l’erreur sur la sortie réservée aux erreurs.

Justification

Maintenabilité. Identification rapide des erreurs.

Traitement efficace des retours d'erreurs qui peuvent en étant considérée comme des valeurs de sortie normale entraîner des catastrophes.

Utiliser le mécanisme d'assertion (c_rob_7)

Les assertions sont des tests à placer dans vos programmes. C'est une condition qui doit être obligatoirement vérifiée. Dans le cas contraire le programme s'arrêtera.

Le langage C implémente les assertion à l'aide d'une macro : assert(condition) qui en plus est débrayable.

Justification

Vous devez utiliser ce mécanisme d'assertion pour construire des programmes plus robustes et faciliter la maintenance et la relecture du code.

Lorsque vous concevez une fonction, vous identifiez des invariants : des conditions qui doivent toujours être vérifiées pour pouvoir exécuter le traitement ou fournir des résultats utilisables. Par exemple pour une loi physique : une température doit toujours être supérieure ou égale à 0 Kelvin.

Vous coderez ces conditions sous forme d'assertion. En phase de mise au point, si une assertion n'est pas vérifiée, le programme ne doit pas se poursuivre. Lors de la phase de test, lorsqu'une assertion n'est pas vérifiée, le programmeur doit alors coder un mécanisme permettant de présenter un message d'erreur clair ou un contournement qui permettra de continuer. Les assertions qui n'auront pas été déclenchées en fin de période de test seront laissées dans les sources.