Dernièrement, j'ai commencé à utiliser la charpie pour l'analyse de code statique .Un des avertissements que je reçois concerne parfois ce problème . Disons par exemple que j'ai la fonction suivante:
uint32_t foo( void );
Et disons que j'ignore délibérément la valeur de retour de la fonction . Pour faire disparaître l'avertissement, on peut écrire
(void) foo();
Ma question est la suivante: quelle est la "bonne" façon d'écrire un code comme celui-ci, devrais-je continuer comme je le faisais toujours, puisque le compilateur ne s'en plaint pas, ou devrais-je utiliser le vide pour plus de clarté, afin que les autres responsables du code le sachent? que j'ai délibérément ignoré la valeur de retour.
Quand je regarde le code comme ceci (avec le vide), cela me semble assez étrange ...
La méthode la plus courante consiste simplement à appeler foo();
sans transtyper en (void)
.
Celui qui n'a jamais ignoré la valeur de retour de printf()
, jette la première pierre.
Personnellement, j'aime bien les avertissements "inutilisés", mais il arrive parfois que je doive les ignorer (par exemple, le write()
à l'utilisateur, ou le fscanf(...,"%*s\n")
ou le strtol()
où la valeur de retour est sans importance et je veux juste l'effet secondaire de [peut-être] déplacer le pointeur de fichier le long.)
Avec gcc 4.6, cela devient assez compliqué.
(void)
ne fonctionne plus.{ssize_t ignore; ignore=write(...);}
émet un autre avertissement (assigné-non-utilisé).write(...)+1
émet un autre avertissement (valeur calculée, non utilisée).Le seul bon moyen (si moche) de les supprimer est de convertir la valeur de retour en quelque chose que le compilateur convient que vous pouvez ignorer.
Par exemple, (void)(write(...)+1)
.
C'est apparemment un progrès. (Et +0
ne fonctionne pas, BTW.)
Une façon de faire cela avec les compilateurs Clang et GCC est d'utiliser un pragma
:
/* ... */
#pragma GCC diagnostic Push
#pragma GCC diagnostic ignored "-Wunused-result"
foo(); /* this specific unused-result warning gets ignored during compilation */
#pragma GCC diagnostic pop
/* ... */
La combinaison Push
-pop
encapsule la directive ignored
de sorte que des avertissements puissent être déclenchés ailleurs dans votre code. Il devrait être plus facile pour quiconque de lire votre code source de voir ce que fait ce bloc de code.
Pour qu'un vérificateur de code statique soit utile, il doit également signaler les valeurs de retour ignorées, ce qui peut souvent rendre difficile le suivi des erreurs - ou le traitement des erreurs manquantes.
Donc, vous devriez garder le (void)
ou désactiver le contrôle pour printf
. Vous avez maintenant plusieurs options pour le faire de manière lisible. J'utilise pour envelopper la fonction dans une nouvelle fonction comme par exemple
void printf_unchecked(const char* format, ...)
où le casting pas si gentil a lieu. Peut-être est-il plus pratique de le faire en utilisant une macro de préprocesseur dans ce cas en raison des varargs ...
Une façon un peu plus "belle" d'indiquer les résultats non utilisés serait la suivante:
/**
* Wrapping your function call with ignore_result makes it more clear to
* readers, compilers and linters that you are, in fact, ignoring the
* function's return value on purpose.
*/
static inline void ignore_result(long long int unused_result) {
(void) unused_result;
}
...
ignore_result(foo());
Avec C++
, cela peut être étendu à:
template<typename T>
inline void ignore_result(T /* unused result */) {}
J'aime compiler mes codes avec les drapeaux:
$gcc prog1.c -o prog1.x -Wall -Wextra -ansi -pedantic-errors -g -O0 -DDEBUG=1
Et pour éviter -Wunused-result
je n'aime pas l'idée d'ajouter un autre drapeau: -Wno-unused-result
(si c'est le cas, c'est une solution).
J'avais l'habitude de transtyper en (void)
pour certaines fonctions (pas printf
ou autre célèbre, car les compilateurs n'en avertissent pas, mais les étranges). La conversion en (void)
ne fonctionne plus (GCC 4.7.2)
Attelle drôle conseille:
Result returned by function call is not used. If this is intended,
can cast result to (void) to eliminate message. (Use -retvalother to
inhibit warning)
Mais ce n'est plus une solution. Splint a besoin d'une mise à jour concernant ce problème.
Donc, pour se débarrasser de l’avertissement d’une manière très compatible, voici une bonne MACRO
:
/** Turn off -Wunused-result for a specific function call */
#define igr(M) if(1==((int)M)){;}
Et appelez ça comme ça:
igr(PL_get_chars(t, &s, CVT_VARIABLE));
C'est un look épuré, et tout compilateur éliminera le code. Ci-dessous une photo de mon éditeur préféré vi
: fenêtre de gauche, pas de igr()
; fenêtre du milieu, en utilisant igr()
; fenêtre de droite, source.
Vous pouvez voir, c’est exactement la même chose, un code totalement inoffensif qui permet à C de faire ce que gcc ne laissera pas: ignorer le code de retour.
La comparaison 1==...
est nécessaire uniquement pour éviter une attelle qui avertit que cette condition est non BOOL
. GCC s'en fout. Selon la fonction, vous pourriez recevoir un avertissement cast
. J'ai fait un test en ignorant une double
avec cette MACRO et c'était bon, mais je ne suis pas totalement convaincu. Surtout si la fonction retourne un pointeur ou quelque chose de plus complexe.
Dans ce cas, vous aurez également besoin de:
#define pigr(M) if(NULL==((void *)M)){;}
Dernière chose: le {;}
est nécessaire en raison de l’avertissement -Wempty-body
(suggérez des accolades autour du corps vide dans une instruction ‘if’).
Et (maintenant la dernière chose), le ;
après l'appel de fonction n'est pas (strictement) nécessaire, mais constitue une bonne pratique. Rend vos lignes de code plus homogènes, toutes finissant par un ;
. (Il est traduit par un mnémonique NOP
et, après optimisation, disparaît).
L'exécution du compilateur ne donne aucun avertissement ou erreur. Runing splint
donne:
$ splint ./teste.c -I/usr/lib/swi-prolog/include/ -strict-lib
Splint 3.1.2 --- 20 Feb 2009
Finished checking --- no warnings
Voir aussi cette réponse
gnulib a ceci: http://git.savannah.gnu.org/cgit/gnulib.git/tree/lib/ignore-value.h
/* Normally casting an expression to void discards its value, but GCC
versions 3.4 and newer have __attribute__ ((__warn_unused_result__))
which may cause unwanted diagnostics in that case. Use __typeof__
and __extension__ to work around the problem, if the workaround is
known to be needed. */
#if 3 < __GNUC__ + (4 <= __GNUC_MINOR__)
# define ignore_value(x) \
(__extension__ ({ __typeof__ (x) __x = (x); (void) __x; }))
#else
# define ignore_value(x) ((void) (x))
#endif
Il n’ya généralement pas trop de fonctions dont vous voulez ignorer la valeur. Splint , par exemple, permet d'ajouter un commentaire spécial lui permettant de savoir qu'une valeur renvoyée par une fonction spécifique pourrait être ignorée. Malheureusement, cela désactive en réalité tous les avertissements «ignoré de la valeur renvoyée» liés à cette fonction particulière.
Voici un exemple de Splint-clean programme:
#include <stdio.h>
FILE /*@alt void@*/ *fopen(const char *path, const char *mode);
static int /*@alt void@*/ test(void)
{
printf( "test called\n" );
fopen( "test", "a" );
return 0;
}
int main(void)
{
test();
return 0;
}
La partie désagréable est que vous devez ajouter un prototype supplémentaire à une fonction système avec un commentaire quelque part.
Btw, par défaut Splint ne se plaint pas de la valeur de retour de printf
et de certaines autres fonctions de la libc non utilisées. Cependant, un mode plus strict peut être activé.
LINT permet quelque chose de similaire, mais je ne l'ai jamais utilisé. Voici ce que dit une documentation.
LINT vous permet de marquer des fonctions avec des valeurs de retour facultatives en utilisant un directive similaire aux #directives du préprocesseur C.
#pragma optresult
peut être placé immédiatement avant la définition d'une fonction qui renvoie un résultat optionnel. LINT reconnaît alors que cette fonction renvoie un résultat qui peut être ignoré; LINT ne donne pas d'erreur messages si le résultat est ignoré.
Une autre solution serait d'utiliser réellement une valeur. Ensuite, vous pouvez supprimer l'avertissement unused variable
avec une macro.
#define _unused(x) ((void)(x))
Ensuite, dans votre exemple, vous auriez:
val = foo();
_unused(val);