do { stuff() } while(0);
делает то же самое, что и stuff (). Так в чем же тогда дело? Проблема в синтаксисе макросов. Предположим, мы определили макрос как:
#define SAFE_FREE(x) if ((x) != NULL) { free(x); x=NULL; }
Тогда есть две проблемы. Первый относительно второстепенный: для использования SAFE_FREE больше не требуется конечная точка с запятой. Что еще более важно, код вроде:
if (...)
SAFE_FREE(x)
else
stuff();
расширится до:
if (...)
if ((x) != NULL) {
free(x);
x = NULL;
} else
stuff();
Определение макроса, как указано выше, предотвращает странное поведение, как указано выше, поскольку do {...} while (0)
действует точно так же, как заявление без точки с запятой.
Кстати, в FAQ по стилю и технике C ++ Бьярн Страуструп предлагает использовать встроенную (шаблонную) функцию для "удаления и обнуления"
template<class T> inline void destroy(T*& p) { delete p; p = 0; }
do while - это общепринятое соглашение, согласно которому макрос требует конечной точки с запятой, как это сделала бы стандартная функция c. Помимо этого, он просто гарантирует, что освобожденная переменная установлена в NULL, чтобы любые будущие вызовы для ее освобождения не вызывали ошибок.
Идея do / while (0) заключается в том, что вы можете использовать макрос, в котором вы будете использовать вызов функции без неожиданных ошибок.
Например, если у вас был код вида:
if (today_is_tuesday())
SAFE_FREE(x);
else
eat_lunch();
, а макрос был просто:
#define SAFE_FREE(x) if (x) { free(x); x = 0; }
, вы бы получили совсем другой результат. Соглашение do / while позволяет избежать этих ошибок, заставляя его вести себя согласованно.