do {...} while (0): ¿para qué sirve? [duplicar]
He estado viendo esa expresión desde hace más de 10 años. He estado tratando de pensar para qué sirve. Como lo veo principalmente en #defines, supongo que es bueno para la declaración de variables de alcance interno y para usar saltos (en lugar de gotos).
¿Sirve para algo más? ¿Lo usas?
Es la única construcción en C que puede usar para #define
una operación de múltiples declaraciones, poner un punto y coma después y seguir usándola dentro de una if
declaración. Un ejemplo podría ayudar:
#define FOO(x) foo(x); bar(x)
if (condition)
FOO(x);
else // syntax error here
...;
Incluso el uso de frenillos no ayuda:
#define FOO(x) { foo(x); bar(x); }
Usar esto en una if
declaración requeriría omitir el punto y coma, lo cual es contrario a la intuición:
if (condition)
FOO(x)
else
...
Si define FOO así:
#define FOO(x) do { foo(x); bar(x); } while (0)
entonces lo siguiente es sintácticamente correcto:
if (condition)
FOO(x);
else
....
Es una forma de simplificar la verificación de errores y evitar if anidados profundos. Por ejemplo:
do {
// do something
if (error) {
break;
}
// do something else
if (error) {
break;
}
// etc..
} while (0);
Es útil agrupar varias declaraciones en una sola para que una macro similar a una función pueda usarse como una función. Supongamos que tienes:
#define FOO(n) foo(n);bar(n)
y lo hace:
void foobar(int n) {
if (n)
FOO(n);
}
entonces esto se expande a:
void foobar(int n) {
if (n)
foo(n);bar(n);
}
Observe que la segunda llamada ya bar(n)
no forma parte de la if
declaración.
Envuelva ambos en do { } while(0)
y también puede usar la macro en una if
declaración.
Es interesante observar la siguiente situación en la que el bucle do {} while (0) no funcionará:
Si desea una macro similar a una función que devuelva un valor, necesitará una expresión de declaración : ({stmt; stmt;}) en lugar de do {} while(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;
}