344 votes

Pourquoi dois-je toujours activer les avertissements du compilateur ?

J'entends souvent dire que lorsque je compile des programmes C et C++, je dois "toujours activer les avertissements du compilateur". Pourquoi cela est-il nécessaire ? Comment dois-je m'y prendre ?

Parfois, j'entends aussi dire que je devrais "traiter les avertissements comme des erreurs". Devrais-je le faire ? Comment dois-je m'y prendre ?

19voto

Josiah Points 613

Les autres réponses sont excellentes et je ne veux pas répéter ce qu'elles ont dit.

Un autre aspect du "pourquoi activer les avertissements" qui n'a pas été correctement abordé est qu'ils aident énormément à la maintenance du code. Lorsque vous écrivez un programme d'une taille significative, il devient impossible de garder l'ensemble du programme dans votre tête en même temps. Vous avez généralement une ou trois fonctions que vous écrivez activement et auxquelles vous pensez, et peut-être un ou trois fichiers sur votre écran auxquels vous pouvez vous référer, mais la majeure partie du programme existe quelque part en arrière-plan et vous devez faire confiance à son fonctionnement.

Le fait d'avoir des avertissements, et de les avoir aussi énergiques et visibles que possible, permet de vous alerter si quelque chose que vous modifiez crée des problèmes pour quelque chose que vous ne pouvez pas voir.

Prenons par exemple l'avertissement de clang -Wswitch-enum . Cela déclenche un avertissement si vous utilisez un commutateur sur un enum et que vous omettez l'une des valeurs possibles de l'enum. On pourrait penser qu'il s'agit d'une erreur peu probable : vous avez probablement au moins regardé la liste des valeurs de l'énumération lorsque vous avez écrit l'instruction switch. Vous avez peut-être même un IDE qui génère les options de commutation pour vous, ne laissant aucune place à l'erreur humaine.

Cet avertissement prend tout son sens lorsque, six mois plus tard, vous ajoutez une autre entrée possible à l'énumération. Encore une fois, si vous pensez au code en question, tout ira probablement bien. Mais si cette énumération est utilisée à plusieurs fins différentes et que c'est pour l'une d'entre elles que vous avez besoin d'une option supplémentaire, il est très facile d'oublier de mettre à jour un commutateur dans un fichier que vous n'avez pas touché depuis 6 mois.

Vous pouvez considérer les avertissements de la même manière que les scénarios de test automatisés : ils vous aident à vous assurer que le code est sensé et fait ce dont vous avez besoin lorsque vous l'écrivez pour la première fois, mais ils vous aident encore plus à vous assurer qu'il continue à faire ce dont vous avez besoin pendant que vous l'étudiez. La différence est que les scénarios de test fonctionnent de manière très étroite par rapport aux exigences de votre code et que vous devez les écrire, alors que les avertissements fonctionnent de manière large par rapport à des normes raisonnables pour presque tout le code, et ils sont très généreusement fournis par les ingénieurs qui font les compilateurs.

9 votes

L'autre façon dont ils aident à la maintenance est lorsque vous regardez le code de quelqu'un d'autre et que vous ne pouvez pas dire si un effet secondaire était intentionnel. Si les avertissements sont activés, vous savez qu'ils étaient au moins conscients du problème.

0 votes

Ou dans mon cas, vous importez un fichier d'un système embarqué qui contient une instruction switch de plus de 3000 lignes sur une énumération avec plusieurs milliers de valeurs. Les avertissements "falls through" (évités par l'utilisation de goto) masquaient un certain nombre de bogues "non traités"... le compilateur embarqué n'émettait aucun de ces bogues, mais ils étaient néanmoins importants.

16voto

Dmitry Grigoryev Points 1495

Traiter les avertissements comme des erreurs n'est qu'un moyen d'autodiscipline : vous compiliez un programme pour tester cette nouvelle fonctionnalité brillante, mais vous ne peut pas jusqu'à ce que vous répariez les parties négligées. Il n'y a aucune information supplémentaire Werror fournit, il établit juste des priorités très clairement :

N'ajoutez pas de nouveau code avant d'avoir résolu les problèmes du code existant.

C'est vraiment l'état d'esprit qui est important, pas les outils. La sortie des diagnostics du compilateur est un outil. MISRA (pour le C embarqué) est un autre outil. Peu importe lequel vous utilisez, mais on peut dire que les avertissements du compilateur sont l'outil le plus facile à utiliser (il suffit d'un seul drapeau à activer) et le rapport signal/bruit est très élevé. Il n'y a donc aucune raison no pour l'utiliser.

Aucun outil n'est infaillible. Si vous écrivez const float pi = 3.14; La plupart des outils ne vous diront pas que vous avez défini avec une mauvaise précision, ce qui peut entraîner des problèmes par la suite. La plupart des outils ne sourcilleront pas sur les points suivants if(tmp < 42) même s'il est communément admis que donner aux variables des noms dénués de sens et utiliser des chiffres magiques est un moyen de courir à la catastrophe dans les grands projets. Vous doivent comprendre que tout code de "test rapide" que vous écrivez n'est que cela : un test, et que vous devez le réussir avant de passer à d'autres tâches, alors que vous voyez encore ses défauts. Si vous laissez ce code tel quel, le déboguer si vous passez deux mois à ajouter de nouvelles fonctionnalités sera nettement plus difficile.

Une fois que vous êtes dans le bon état d'esprit, il n'y a pas de raison d'utiliser Werror . Le fait d'avoir des avertissements en tant que tels vous permettra de décider en connaissance de cause s'il est toujours utile d'exécuter la session de débogage que vous étiez sur le point de lancer, ou de l'interrompre et de corriger les avertissements d'abord.

3 votes

Pour le meilleur ou pour le pire, le clippy L'outil de linting pour Rust va effectivement avertir de la présence de la constante "3.14". Il s'agit en fait d'une exemple dans la documentation . Mais comme vous pouvez le deviner d'après le nom, clippy est fier d'être agressivement serviable.

1 votes

@emk Merci pour cet exemple, je devrais peut-être reformuler ma réponse dans un ne jamais dire "jamais" d'une certaine manière. Je ne voulais pas dire que la vérification des valeurs imprécises est impossible, mais simplement que le fait de se débarrasser des avertissements ne garantit pas une qualité de code décente.

0 votes

Une chose que les avertissements en tant qu'erreurs vous donnent est que les constructions automatisées échoueront, vous alertant ainsi que quelque chose a mal tourné. Les compilations automatisées permettent également d'automatiser le linting (l'explosion est 3...2...1.. :).

9voto

Dom Points 714

Pour quelqu'un qui travaille avec du code C intégré, l'activation des avertissements du compilateur a permis de mettre en évidence de nombreuses faiblesses et des zones à étudier pour proposer des corrections. Dans gcc, l'utilisation de -Wall y -Wextra et même -Wshadow sont devenus vitaux. Je ne vais pas passer en revue tous les dangers, mais je vais en citer quelques-uns qui sont apparus et qui ont permis de mettre en évidence des problèmes de code.

Variables laissées de côté

Celui-ci peut facilement indiquer un travail inachevé et des zones qui n'utilisent pas toutes les variables passées, ce qui peut être un problème. Examinons une fonction simple qui peut déclencher cela :

int foo(int a, int b)
{
   int c = 0;

   if (a > 0)
   {
        return a;
   }
   return 0;
}

La compilation sans -Wall ou -Wextra ne pose aucun problème. -Wall vous dira cependant que c n'est jamais utilisé :

foo.c : Dans la fonction 'foo' :

foo.c:9:20 : avertissement : variable 'c' inutilisée [-Wunused-variable]

-Wextra vous dira également que votre paramètre b ne fait rien :

foo.c : Dans la fonction 'foo' :

foo.c:9:20 : avertissement : variable 'c' inutilisée [-Wunused-variable]

foo.c:7:20 : warning : unused parameter 'b' [-Wunused-parameter] int foo(int a, int b)

Ombrage global variable

Celui-ci a mordu fort et n'est pas apparu jusqu'à ce que -Wshadow a été utilisé. Modifions l'exemple ci-dessus pour ne faire qu'ajouter, mais il se trouve qu'un global porte le même nom qu'un local, ce qui entraîne une grande confusion lorsqu'on essaie d'utiliser les deux.

int c = 7;

int foo(int a, int b)
{
   int c = a + b;
   return c;
}

Lorsque le paramètre -Wshadow a été activé, il est facile de repérer ce problème.

foo.c:11:9 : avertissement : la déclaration de 'c' fait de l'ombre à une déclaration globale [-Wshadow]

foo.c:1:5 : note : la déclaration shadowée est ici

Chaînes de format

Cela ne nécessite pas de drapeaux supplémentaires dans gcc, mais cela a quand même été la source de problèmes dans le passé. Une simple fonction essayant d'imprimer des données, mais ayant une erreur de formatage pourrait ressembler à ceci :

void foo(const char * str)
{
    printf("str = %d\n", str);
}

Cela n'imprime pas la chaîne de caractères puisque le drapeau de formatage est incorrect et gcc vous dira joyeusement que ce n'est probablement pas ce que vous vouliez :

foo.c : Dans la fonction 'foo' :

foo.c:10:12 : warning : format '%d' attend un argument de type un argument de type 'int', mais l'argument 2 est de type 'const char *'. [-Wformat=]


Ce ne sont que trois des nombreuses choses que le compilateur peut vérifier pour vous. Il y en a beaucoup d'autres, comme l'utilisation d'une variable non initialisée, que d'autres ont déjà signalées.

7 votes

Dans le monde de l'embarqué, les avertissements qui m'inquiètent le plus sont " possible loss of precision " et " comparison between signed and unsigned avertissements ". J'ai du mal à comprendre combien de "programmeurs" les ignorent (en fait, je ne sais pas vraiment pourquoi ce ne sont pas des erreurs).

3 votes

Dans ce dernier cas, @Mawg, je crois que la principale raison pour laquelle il ne s'agit pas d'une erreur est que le résultat de la commande sizeof est non signé, mais le type d'entier par défaut est signé. Le site sizeof type de résultat, size_t est généralement utilisé pour tout ce qui concerne la taille du type, comme, par exemple, l'alignement ou le nombre d'éléments d'un tableau/conteneur, tandis que les entiers en général sont destinés à être utilisés comme " int sauf indication contraire". Si l'on considère le nombre de personnes à qui l'on apprend ainsi à utiliser int pour itérer sur leurs conteneurs (comparer int a size_t ), en faire une erreur casserait à peu près tout ;P

7voto

Mark Diaz Points 38

LES AVERTISSEMENTS DU COMPILATEUR SONT VOTRE AMI (sans crier, en majuscules pour l'accentuation).

Je travaille sur d'anciens systèmes Fortran-77. Le compilateur me dit des choses précieuses : des erreurs de type de données d'argument sur un appel de sous-routine, l'utilisation d'une variable locale avant qu'une valeur ait été définie dans la variable, si j'ai une variable ou un argument de sous-routine qui n'est pas utilisé. Ce sont presque toujours des erreurs.

Pour éviter un long post : Quand mon code se compile proprement, il fonctionne à 97%. L'autre gars avec qui je travaille compile avec tous les avertissements désactivés, passe des heures ou des jours dans le débogueur, puis me demande de l'aide. Je compile simplement son code avec les avertissements et je lui dis ce qu'il doit corriger.

7voto

Joshua Points 13231

Il s'agit d'une réponse spécifique à C, et de la raison pour laquelle cela est bien plus important pour C que pour toute autre chose.

#include <stdio.h>
int main()
{
   FILE *fp = "some string";
}

Ce code se compile avec un avertissement . Ce qui est et devrait être des erreurs dans à peu près tous les autres langages de la planète (à l'exception du langage d'assemblage) sont les suivants avertissements en C. Les avertissements en C sont presque toujours des erreurs déguisées. Les avertissements doivent être corrigés, pas supprimés.

Avec gcc nous le faisons comme suit gcc -Wall -Werror .

C'est aussi la raison pour laquelle on a beaucoup râlé à propos de certains avertissements d'API non sécurisés de MS. La plupart des gens qui programment en C ont appris à la dure à traiter les avertissements comme des erreurs et ce genre de choses est apparu qui n'était tout simplement pas le même genre de choses et qui voulait des corrections non portables.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X