web-dev-qa-db-fra.com

Pourquoi (void) est une opération no en C et C++?

J'ai vu debug printfs dans glibc, qui est défini en interne comme (void) 0, si NDEBUG est défini. De même, le compilateur __noop for Visual C++ est également présent. Le premier fonctionne à la fois sur les compilateurs GCC et VC++, tandis que le dernier ne fonctionne que sur VC++. Maintenant, nous savons tous que les deux instructions ci-dessus seront traitées comme aucune opération et qu'aucun code respectif ne sera généré. mais voici où j'ai un doute.

Dans le cas de __noop, MSDN indique qu'il s'agit d'une fonction intrinsèque fournie par le compilateur. Venir à (void) 0 ~ Pourquoi est-il interprété par les compilateurs comme non op? Est-ce un usage délicat du langage C ou la norme en dit-elle explicitement? Ou même cela a quelque chose à voir avec l'implémentation du compilateur?

45
legends2k

(void)0 (+ ;) est une expression C++ valide, mais qui ne fait rien, c'est tout. Il ne se traduit pas par l'instruction no-op de l'architecture cible, il s'agit simplement d'une instruction vide en tant qu'espace réservé lorsque le langage attend une instruction complète (par exemple, en tant que cible d'une étiquette de saut ou dans le corps d'une clause if.).

EDIT: (mis à jour sur la base du commentaire de Chris Lutz)

Il convient de noter que, lorsqu'il est utilisé comme macro, disons

#define noop ((void)0)

le (void) l'empêche d'être utilisé accidentellement comme une valeur comme

int x = noop;

Pour l'expression ci-dessus, le compilateur le signalera à juste titre comme une opération non valide. GCC crache error: void value not ignored as it ought to be et VC++ aboie 'void' illegal with all types.

61
Alexander Gessler

Toute expression qui n'a pas d'effets secondaires peut être traitée comme une non-opération par le compilateur, qui n'a pas à générer de code (bien que cela puisse être le cas). Il arrive donc que le compilateur (et les humains) ne voient pas d'inconvénient à ce que l'utilisation du résultat de la transtypage ait lieu.

8
anon

Je pense que vous parlez de glibc , pas glib , et la macro en question est la macro assert:

Dans le <assert.h> de la glibc, où NDEBUG (pas de débogage) est défini, assert est défini comme suit:

#ifdef NDEBUG
#if defined __cplusplus && __GNUC_PREREQ (2,95)
# define __ASSERT_VOID_CAST static_cast<void>
#else
# define __ASSERT_VOID_CAST (void)
#endif
# define assert(expr)           (__ASSERT_VOID_CAST (0))
#else
/* more code */
#endif

ce qui signifie fondamentalement que assert(whatever); est équivalent à ((void)(0)); et ne fait rien.

A partir de la norme C89 (section 4.2):

L’en-tête <assert.h> définit la macro assert et fait référence à une autre macro,

NDEBUG

qui n'est pas défini par <assert.h>. Si NDEBUG est défini en tant que nom de macro au point du fichier source où <assert.h> est inclus, la macro assert est simplement définie comme suit:

#define assert(ignore) ((void)0)

Je ne pense pas que définir une macro d'impression de débogage égale à (void)0 ait beaucoup de sens. Pouvez-vous nous montrer où cela est fait?

2
Alok Singhal

Même si c'est le cas, pourquoi le dactylographier pour l'annuler? Aussi, dans le cas de #define dbgprintf (void) 0, quand il s'appelle comme dbgprintf ("Hello World!"); -> (vide) 0 ("Hello World!"); - Qu'est-ce que ça veut dire? - legends2k

Les macros remplacent votre code par autre chose. Par conséquent, si vous définissez dbgprint (qui accepte x) en tant que

vide (0)

alors aucune réécriture de X ne se produira en remplacement, donc dbgprintf ("Helloworld") ne sera pas converti en (void) 0 ("Hello world"), mais en (void) 0; - non seulement le nom de macro dbgprint est remplacé par (void) 0, mais tout l'appel dbgprintf ("...")

1
Darko Maksimovic

Sous Windows, j'essaie du code comme celui-ci dans Main.cpp:

#include <iostream>
#define TRACE ((void)0)
int main() {
  TRACE("joke");
  std::cout << "ok" << std::endl;
  return 0;
}

Ensuite, je construis le fichier exe Release version avec la sortie Main.i. Dans le fichier Main.i, la macro TRACE a été remplacée par: ((void)0)("joke") et Visual Studio affiche un avertissement: "avertissement C4353: extension non standard utilisée: constante 0 comme expression de fonction. Utilisez plutôt" __noop ', fonction intrinsèque ". Exécutez le fichier exe, imprimez sur la console les caractères "ok". Donc, je pense que tout est clair: la définition de la macro TRACE [#define TRACE ((void) 0)] est illégale selon la syntaxe c ++, mais le compilateur c ++ de visual studio prend en charge ce comportement comme une extension de compilateur. Ma conclusion est donc la suivante: [#define TRACE ((void) 0)] est une déclaration c ++ illégale, et vous ne l'utilisez PAS au mieux. Mais [#define TRACE (x) ((vide) 0)] est une déclaration légale. C'est tout.

0
baojing.zhou