2008-11-02 2 views
239

Possible Duplicate:
Why are there sometimes meaningless do/while and if/else statements in C/C++ macros?do {...} while (0) - для чего это полезно?

Я видел это выражение уже более 10 лет. Я пытался подумать, для чего это полезно. Поскольку я вижу это в основном в #defines, я предполагаю, что он хорош для объявления внутренней видимости области и для использования перерывов (вместо gotos.)

Хорошо ли для чего-нибудь еще? Вы используете его?

+0

Посмотрите [этот вопрос] (http://stackoverflow.com/questions/243967/do-you-consider-this-technique -Плохо). – 2008-11-02 21:38:10

+7

На самом деле, это не дубликат, поскольку связанный q/a не является специфичным для определения. Легко сравнить оба ответа, чтобы утверждать, что это не дубликат. – Doomsday 2012-05-18 15:44:32

+0

См. «Декрет_используется» в строке Redis 53 [ссылка] https://github.com/antirez/redis-tools/blob/master/zmalloc.c – yet 2012-12-09 21:57:02

ответ

370

Это единственная конструкция в C, которую вы можете использовать для работы в многопользовательском режиме, положить точку с запятой и по-прежнему использовать в заявлении if.Примером может помочь:

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

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

Даже с помощью фигурных скобок не помогает:

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

Используя это в if заявление потребует опустить точку с запятой, которая противоречит здравому смыслу:

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

Если вы определили FOO следующим образом:

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

тогда следующее синтаксически корректен:

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

Это способ упростить проверку ошибок и избежать глубоких вложенных if. Например:

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

В общем, do/while хорош для любого типа конструкции петли, где один должен выполнить цикл по крайней мере один раз. Эмуляция такого типа может происходить либо через прямой while, либо даже в цикле for, но часто результат немного менее изящный. Я признаю, что конкретные приложения этого шаблона довольно редки, но они существуют. Один из них приходит на ум, это меню на основе консольного приложения:

do { 
    char c = read_input(); 

    process_input(c); 
} while (c != 'Q'); 
56

Это помогает группировать несколько операторов в один, так что функция типа макросов на самом деле может быть использован в качестве функции. Предположу, у вас есть

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

и вы

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

этот расширяются

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

Обратите внимание, что второй вызов (бар (п)) не является частью, если заявления больше ,

Оберните оба в do {} while (0), и вы также можете использовать макрос в выражении if.

16

Интересно отметить такую ​​ситуацию, когда делать {}, а (0) петля не будет работы для вас:

Если вы хотите использовать макрос функции, который возвращает значение, тогда вам понадобится statement expression: ({stmt; STMT;}) вместо дел {}, а (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; 
}