Why we use of do while(0) when we define a macro?
#define INIT_LIST_HEAD(ptr) do { \ (ptr)->next = (ptr); (ptr)->prev = (ptr); \ } while (0)
Why do they use this rather than define it simply in a {}?
You can follow it with a semicolon and make it look and act more like a function. It also works with if/else clauses properly then.
Without the while(0), your code above would not work with
if (doit) INIT_LIST_HEAD(x); else displayError(x);
since the semicolon after the macro would "eat" the else clause, and the above wouldn't even compile.
========
Зачем нужна конструкция do { … } while(0)?
/* * All of the callbacks setting methods can be generalized to this: */ #define SET_CALLBACK(a) \ do \ { \ if( fgStructure.CurrentWindow == NULL ) \ return; \ SET_WCB( ( *( fgStructure.CurrentWindow ) ), a, callback ); \ } while( 0 )
Q: Не понимаю, зачем здесь нужен цикл, ведь он в любом случае выполниться всего одинраз, разве нет?
A:
Конструкция широко используется в "функциональных" макросах и предназначена для объединения нескольких statement и объявлений внутри макроса в один составной statement. Такое объединение нужно для того, чтобы макрос можно было использовать как обычный вызов функции в ветках условного оператора, циклах и т.п. без необходимости постоянно заключать этот вызов во внешнюю пару { ... }
На первый взгляд того же самого можно было бы достичь просто путем заключения самого определения макроса в пару { ... } вместо do { ... } while (0). Однако вариант с { ... } будет обладать одним неприятным недостатком - надо будет постоянно помнить, что его тело представляет из себя { ... }, а после { ... } в С не всегда можно ставить ;.
Например, если бы тело SET_CALLBACK было заключено просто в { ... }, то вот такой естественно выглядящий код не скомпилировался бы по причине того, что из-за поставленной после SET_CALLBACK(foo) точки с запятой ветка else оказалось оторванной от своего if
if (true) SET_CALLBACK(foo); // <- "Лишняя" `;` else // <- Ошибка: оторванный `else` /* что-то еще */;
Вот тут-то на сцену и выходит do { ... } while (0). Цикл do/while уникален тем, что является единственной (почти) в языке С грамматической конструкцией, которая формирует блок, и при этом всегда безусловно заканчивается на ;. Благодаря этой замыкающей ; мы можем более естественным образом использовать do { ... } while (0) внутри составных макросов для объединения их в единый statement. В вышеприведенном примере с if ;, указанная после SET_CALLBACK(foo), будет "поглощена" конструкцией do { ... } while (0) и не приведет в "разрушению" целостности if.
Вот именно ради того, чтобы иметь возможность "вызывать" функциональные макросы через тот же естественный синтаксис, через который мы вызываем обычные функции, тело составного макроса и заключают не в { ... }, а именно в do { ... } while (0)
P.S. Почему авторы языка в свое время решили настоять на включении [теоретически совсем не нужной] замыкающей ; в синтаксис do/while - не совсем ясно, но это было сделано.