Duplicate possible:
Pourquoi y a-t-il parfois des instructions sans signification dans les macros C/C++?// Et/si/else?
Je vois cette expression depuis plus de 10 ans maintenant. J'ai essayé de penser à ce que c'est bon pour. Comme je le vois principalement dans #defines, je suppose que c'est bon pour la déclaration de variable de portée interne et pour l'utilisation de sauts (au lieu de gotos.)
Est-ce bon pour autre chose? L'utilisez vous?
C'est la seule construction en C que vous pouvez utiliser pour #define
une opération multi-déclarations, placer un point-virgule après et continuer à être utilisé dans une instruction if
. Un exemple pourrait aider:
#define FOO(x) foo(x); bar(x)
if (condition)
FOO(x);
else // syntax error here
...;
Même utiliser des accolades n'aide pas:
#define FOO(x) { foo(x); bar(x); }
Utiliser ceci dans une instruction if
nécessiterait que vous omettiez le point-virgule, ce qui est contre-intuitif:
if (condition)
FOO(x)
else
...
Si vous définissez FOO comme ceci:
#define FOO(x) do { foo(x); bar(x); } while (0)
alors ce qui suit est syntaxiquement correct:
if (condition)
FOO(x);
else
....
C'est un moyen de simplifier la vérification des erreurs et d'éviter les if imbriqués profonds. Par exemple:
do {
// do something
if (error) {
break;
}
// do something else
if (error) {
break;
}
// etc..
} while (0);
Cela permet de regrouper plusieurs instructions en une seule, de sorte qu'une macro de type fonction puisse réellement être utilisée en tant que fonction. Supposons que vous ayez
#define FOO(n) foo(n);bar(n)
et vous faites
void foobar(int n){
if (n)
FOO(n);
}
alors cela se développe à
void foobar(int n){
if (n)
foo(n);bar(n);
}
Notez que le second appel (bar (n)) ne fait plus partie de l'instruction if.
Encapsulez les deux dans do {} while (0) et vous pourrez également utiliser la macro dans une instruction if.
Il est intéressant de noter la situation suivante dans laquelle la boucle do {} while (0) ne fonctionnera pas :
Si vous voulez une macro semblable à une fonction qui renvoie une valeur, vous aurez besoin de expression de déclaration : ({stmt; stmt;}) à la place de {} tandis que (0):
#include <stdio.h>
#define log_to_string1(str, fmt, arg...) \
do { \
sprintf(str, "%s: " fmt, "myprog", ##arg); \
} while (0)
#define log_to_string2(str, fmt, arg...) \
({ \
sprintf(str, "%s: " fmt, "myprog", ##arg); \
})
int main() {
char buf[1000];
int n = 0;
log_to_string1(buf, "%s\n", "No assignment, OK");
n += log_to_string1(buf + n, "%s\n", "NOT OK: gcc: error: expected expression before 'do'");
n += log_to_string2(buf + n, "%s\n", "This fixes it");
n += log_to_string2(buf + n, "%s\n", "Assignment worked!");
printf("%s", buf);
return 0;
}