web-dev-qa-db-fra.com

Ignorer les valeurs de retour en C

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 ...

39
stdcall

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.

52
mouviciel

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é.

  • La conversion en (void) ne fonctionne plus.
  • Réécrire des fonctions (surtout variadiques) est fastidieux et maladroit.
  • {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.)

25
dfsmith

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.

14
Alex Reynolds

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 ...

8
jdehaan

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 */) {}
7
mic_e

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.

enter image description here

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

4
Dr Beco

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
3
Eric Blake

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é.

1
Maksim Skurydzin

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); 
0
xyz