do { … } while (0) — ¿para qué sirve? [duplicate]

7 minutos de lectura

avatar de usuario
Gilm

Posible duplicado:

¿Por qué a veces hay declaraciones do/while y if/else sin sentido en las macros de C/C++?

He estado viendo esa expresión durante más de 10 años. He estado tratando de pensar para qué sirve. Dado que lo veo principalmente en #defines, asumo que es bueno para la declaración de variables de alcance interno y para usar saltos (en lugar de gotos).

¿Es bueno para algo más? ¿Lo usas?

  • Echa un vistazo a esta pregunta.

    – Federico A. Ramponi

    2 de noviembre de 2008 a las 21:38

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

    – Día del Juicio Final

    18 mayo 2012 a las 15:44

  • Ver “decrement_used_memory” de la línea 53 de Redis https://stackoverflow.com/questions/257418/do-while-0-what-is-it-good-forgithub.com/antirez/redis-tools/blob/master/zmalloc.c

    – aún

    9 de diciembre de 2012 a las 21:57

  • El duplicado es con la pregunta marcada como posible duplicado (primera línea del post), no con la pregunta dada por Federico A. Ramponi.

    – Étienne

    2 de mayo de 2013 a las 8:52

  • otras variantes de este do { ... } while ((void)0, 0) se utiliza para silenciar las advertencias del compilador sobre “condición constante”.

    – Amro

    1 de enero de 2016 a las 0:56

avatar de usuario
greg hewgill

Es la única construcción en C que puede usar para #define una operación de múltiples declaraciones, coloque un punto y coma después y aún use dentro de un if declaración. Un ejemplo podría ayudar:

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

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

Ni siquiera el uso de aparatos ortopédicos ayuda:

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

Usar esto en un if declaración requeriría que omita 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
    ....

  • no lo haría #define FOO(x) if(true){ foo(x); bar(x); } else void(0) también funciona aunque es mucho más feo?

    – Adisak

    07/08/2013 a las 22:39


  • @user10607: Sí, eso funciona si sus expansiones de macros son una lista de expresiones. Sin embargo, si desea incluir un if o while dentro de la expansión, ese truco tampoco funcionará.

    – Greg Hewgill

    3 de noviembre de 2014 a las 19:45

  • Sigo pensando que esto es feo, y la codificación SLOPPY, y PUEDE evitarse, si se codifica correctamente. esto se remonta a algunas de las reglas C de sentido común, de SIEMPRE usar curly en sus declaraciones IF, incluso si solo tiene UNA operación después de IF. Esto debería ser una práctica estándar, IMVHO…

    – LarryF

    16/07/2015 a las 20:37

  • @LarryF: El problema es que, si está escribiendo un archivo de encabezado para que lo usen otros, no puede elegir cómo otras personas van a usar su macro. Para evitar sorpresas inesperadas a sus usuarios, debe usar una técnica como la anterior para hacer que su macro se comporte como se comportaría cualquier otra instrucción C.

    – Greg Hewgill

    16 de julio de 2015 a las 23:39

  • @AaronFranke porque {…}; en realidad está hecho de dos declaraciones, la parte {…} y otra declaración vacía después de ella. hacer {…} mientras (0); es una declaración, sin embargo. Larry/Marco cuando los usuarios escriben FOO(a, b, c) esperan que (a menos que se pretenda explícitamente lo contrario) resulte en una expresión, como una llamada a una función de retorno de vacío. El tuyo es en realidad el enfoque descuidado. La convención de estilo de código de su usuario no debería ser de su incumbencia.

    – yonil

    26 de junio de 2020 a las 10:06

avatar de usuario
Jere.Jones

Es una forma de simplificar la comprobación de errores y evitar if’s profundamente anidados. Por ejemplo:

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

  • er, inclúyalo en un método y use el retorno anticipado.

    -Dustin Getz

    2 de noviembre de 2008 a las 23:35

  • o… simplemente usa do { } while(0).

    – nickf

    2 de noviembre de 2008 a las 23:48

  • O usar goto. No en serio, if (error) goto error; y un error: ... cerca del final me parece una versión más limpia que logra lo mismo.

    – Luciérnaga

    23 de abril de 2014 a las 17:46

  • @WChargin: el código “ir a fallar” en el artículo que vinculó también habría fallado con un “pausa”. Alguien acaba de duplicar una línea allí. No fue culpa de goto.

    – Nicolás M.

    15 de junio de 2014 a las 11:15

  • @NiccoloM. “concedido, goto no fue el error, lo que en realidad lo hace aún más divertido”

    – cargando

    15 de junio de 2014 a las 17:36

avatar de usuario
Martín contra Löwis

Es útil agrupar varias declaraciones en una sola para que una macro similar a una función se pueda usar como una función. Supongamos que tiene:

#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);
}

Note que la segunda llamada bar(n) no es parte del if declaración más.

Envolver ambos en do { } while(0)y también puede usar la macro en un if declaración.

  • Respuesta muy clara. +1

    – Anirudh Ramanathan

    23 de junio de 2015 a las 18:57

  • Claramente entendido. 😉

    – Quién soy

    6 de julio de 2016 a las 6:04

  • ¿Por qué no simplemente poner frenos alrededor? #define FOO(n) {foo(n);bar(n)}

    – endolito

    27 de agosto de 2021 a las 19:21


  • @endolith porque #define FOO(n) {foo(n);bar(n)} se expandiría a {foo(n);bar(n)}. Observe que no hay punto y coma después bar(n).

    – John

    19 oct 2021 a las 12:37


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

Si desea una macro similar a una función que devuelva un valor, necesitará un expresión de declaración: ({stmt; stmt;}) en lugar de hacer {} 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;
}

Genéricamente, do/while es bueno para cualquier tipo de construcción de bucle donde uno debe ejecutar el bucle al menos una vez. Es posible emular este tipo de bucle a través de una recta while o incluso un for bucle, pero a menudo el resultado es un poco menos elegante. Admito que las aplicaciones específicas de este patrón son bastante raras, pero existen. Una que me 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');

  • También está disponible en C#, que no tiene macros. No estoy seguro de por qué alguien rechazó esta respuesta, pero la veo como la respuesta casi correcta, excepto que pasó por alto el “0” explícito en la condición while. Por favor, gente, si rechazan la respuesta de alguien, comenten.

    – Jon Davis

    2 de noviembre de 2008 a las 22:36

  • Yo no fui el que votó negativo; sin embargo, la pregunta es muy específica y la respuesta es verdadera en general pero fuera de contexto.

    – tzot

    2 de noviembre de 2008 a las 23:04

  • La afirmación es correcta pero está fuera de contexto.

    – Nadeem Khan

    18 de marzo de 2019 a las 10:08

  • También está disponible en C#, que no tiene macros. No estoy seguro de por qué alguien rechazó esta respuesta, pero la veo como la respuesta casi correcta, excepto que pasó por alto el “0” explícito en la condición while. Por favor, gente, si rechazan la respuesta de alguien, comenten.

    – Jon Davis

    2 de noviembre de 2008 a las 22:36

  • Yo no fui el que votó negativo; sin embargo, la pregunta es muy específica y la respuesta es verdadera en general pero fuera de contexto.

    – tzot

    2 de noviembre de 2008 a las 23:04

  • La afirmación es correcta pero está fuera de contexto.

    – Nadeem Khan

    18 de marzo de 2019 a las 10:08

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad