2008-11-02 30 views
239

Possible Duplicate:
Why are there sometimes meaningless do/while and if/else statements in C/C++ macros?do {...} while (0) - ¿para qué sirve?

He estado viendo esa expresión durante 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 ámbito interno y para usar descansos (en lugar de gotos)

¿Es bueno para cualquier otra cosa? ¿Lo usas?

+0

Tener un vistazo a [esta cuestión] (http://stackoverflow.com/questions/243967/do-you-consider-this-technique -malo). –

+7

En realidad, no es un duplicado ya que el q/a vinculado no es específico para definir. Es fácil comparar ambas respuestas para indicar que no es un duplicado. – Doomsday

+0

Consulte "decrement_used_memory" de la línea 53 de Redis [enlace] https://github.com/antirez/redis-tools/blob/master/zmalloc.c – yet

Respuesta

370

Es la única construcción en C que puede usar para #define una operación de varias instrucciones, poner un punto y coma después, y todavía usar dentro de una declaración if.Un ejemplo puede ayudar a:

#define FOO(x) foo(x); bar(x) 

if (condition) 
    FOO(x); 
else // syntax error here 
    ...; 

Incluso el uso de llaves no ayuda:

#define FOO(x) { foo(x); bar(x); } 

El uso de este en un comunicado por if requeriría que se omite el punto y coma, lo que es contrario a la intuición:

if (condition) 
    FOO(x) 
else 
    ... 

Si define FOO de esta manera:

#define FOO(x) do { foo(x); bar(x); } while (0) 

entonces el siguiente es sintácticamente correcta:

if (condition) 
    FOO(x); 
else 
    .... 
+4

No '#define FOO (x) if (true) {foo (x); bar (x); } else void (0) 'también funciona a pesar de que es mucho más feo? – Adisak

+1

Pero ¿qué tal '' defina FOO (x) foo (x), barra (x) 'por lo que usa una coma en lugar de un punto y coma. ¿No funcionaría esto bien en un if else? – user10607

+2

@ user10607: Sí, eso funciona si tus macroexpansiones es una lista de * expresiones *. Sin embargo, si desea incluir un 'si' o' mientras' dentro de la expansión, ese truco tampoco funcionará. –

83

Es una forma de simplificar la comprobación de errores y evitar las anidaciones profundas. Por ejemplo:

do { 
    // do something 
    if (error) { 
    break; 
    } 
    // do something else 
    if (error) { 
    break; 
    } 
    // etc.. 
} while (0); 
+10

er, factorícelo en un método y utilice el retorno anticipado. –

+11

o ... simplemente use do {} while (0). – nickf

+40

O use 'goto'. No, en serio, 'si (error) goto error;' y un 'error: ...' cerca del final parece ser una versión más limpia que logra lo mismo, para mí. – FireFly

-4

Genéricamente, do/while es bueno para cualquier tipo de construcción de lazo donde uno debe ejecutar el bucle al menos una vez. Es posible emular este tipo de bucle a través de un while recto o incluso un bucle for, pero a menudo el resultado es un poco menos elegante. Admitiré que las aplicaciones específicas de este patrón son bastante raras, pero existen. Una que viene a la mente es una aplicación de consola basada en menús:

do { 
    char c = read_input(); 

    process_input(c); 
} while (c != 'Q'); 
+0

Está disponible en C#, también, que no tiene macros. No estoy seguro de por qué alguien votó negativamente esta respuesta, pero la veo como la respuesta casi correcta, excepto que pasó por alto el "0" explícito en la condición de tiempo. Por favor, amigos, si votan negativamente por la respuesta de alguien, por favor comenten. –

+11

Yo no fui el que menos votó; sin embargo, la pregunta es muy específica y la respuesta es cierta en general, pero fuera de contexto. – tzot

56

Ayuda a la agrupación de varias instrucciones en una sola, por lo que una macro-función como en realidad se puede utilizar como una función. Suponga que tiene

#define FOO(n) foo(n);bar(n) 

y hacer

void foobar(int n){ 
    if (n) 
    FOO(n); 
} 

entonces este se expande a

void foobar(int n){ 
    if (n) 
    foo(n);bar(n); 
} 

en cuenta que la segunda llamada (barra (n)) no es parte de la sentencia if más .

Envuelva ambos en do {} while (0), y también puede usar la macro en una instrucción if.

+2

Muy clara respuesta. +1 –

+1

Claramente entendido. ;) – Whoami

16

Es interesante observar la siguiente situación en la que el do {} while (0) bucle voluntad no trabajo por usted:

Si quiere una macro de función que devuelve un valor, entonces necesitará un statement expression: ({stmt; prop;}) 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; 
} 
+2

Esta es una extensión de GCC. Sin embargo, en C++ 11 podrías hacer lo mismo con una lambda. – celticminstrel