Что делает "делать {...} while (0)" точно в коде ядра?
Возможные дубликаты:
Что нужно использовать во время (0), когда мы определяем макрос?
Почему иногда существуют бессмысленные операторы do/while и if/else в макросах C/С++?
C многострочный макрос: do/while (0) vs scope block
Я видел много таких способов, как это, ранее я, хотя программист хотел легко выйти из блока кода. Зачем нам нужен цикл {...} while (0)? Мы пытаемся что-то сказать компилятору?
Например, в ядре 2.6.25 Linux включите /asm -ia64/system.h
/*
* - clearing psr.i is implicitly serialized (visible by next insn)
* - setting psr.i requires data serialization
* - we need a stop-bit before reading PSR because we sometimes
* write a floating-point register right before reading the PSR
* and that writes to PSR.mfl
*/
#define __local_irq_save(x) \
do { \
ia64_stop(); \
(x) = ia64_getreg(_IA64_REG_PSR); \
ia64_stop(); \
ia64_rsm(IA64_PSR_I); \
} while (0)
Ответы
Ответ 1
Он всегда используется в макросах, поэтому после вызова требуется точка с запятой, точно так же, как при вызове регулярной функции.
В вашем примере вам нужно написать
__local_irq_save(1);
а
__local_irq_save(1)
приведет к ошибке об отсутствии точки с запятой. Этого не произойдет, если этого не произойдет. Если бы речь шла только о области видимости, достаточно простой фигурной фигурной скобки.
Ответ 2
Он позволяет здесь появиться код:
if(a) __local_irq_save(x); else ...;
// -> if(a) do { .. } while(0); else ...;
Если они просто использовали { .. }
, вы получили бы
if(a) { ... }; else ...;
else больше не будет принадлежать ни одному if
, так как точка с запятой будет следующей операцией и отделяет else
от предшествующего if
. Ошибка компиляции.
Ответ 3
Цель конструкции do{ ... } while(0)
- превратить группу операторов в один составной оператор, который может быть завершен с помощью ;
. Вы видите, что на языке C конструкция do/while
имеет одно странное и необычное свойство: даже если оно "работает" как составной оператор, оно ожидает ;
в конце. Никакие другие составные конструкции в C не обладают этим свойством.
Из-за этого свойства вы можете использовать do/while
для написания макросов с несколькими операторами, которые можно безопасно использовать как "обычные" функции, не беспокоясь о том, что внутри макроса, как в следующем примере
if (/* some condition */)
__local_irq_save(x); /* <- we can safely put `;` here */
else
/* whatever */;
Ответ 4
Ответ уже задан (так что макрос заставляет a ;
при вызове), но другое использование этого вида оператора, которое я видел: он позволяет break быть вызванным в любом месте в "loop", раннее завершение если нужно. По сути, "goto", что ваши коллеги-программисты не убили бы вас.
do {
int i = do_something();
if(i == 0) { break; } // Skips the remainder of the logic
do_something_else();
} while(0);
Обратите внимание, что это все еще довольно запутанно, поэтому я не рекомендую его использовать.
Ответ 5
Похоже, он там только для обзора. Это похоже на:
if (true)
{
// Do stuff.
}
изменить
Я не вижу его в вашем примере, но возможно, что один из этих вызовов функций на самом деле является макросом, и в этом случае существует одно ключевое различие между do/while (0) и if (true), которое первая позволяет continue
и break
.
Ответ 6
Он использует макроактив, как реальный оператор или вызов функции.
Оператор представляет собой либо { expression-list }
, либо expression;
, что создает проблему при определении макросов, которым требуется более одного выражения, потому что если вы используете { }
, тогда синтаксическая ошибка произойдет, если вызывающий макрос достаточно разумно добавляет a ;
перед else.
if(whatever)
f(x);
else
f(y);
Если f()
является одним макросом оператора, отлично, но что, если это макрос и что-то сложное? В итоге вы получите if(...) { s1; s2; }; else ...
, и это не сработает.
Итак, писатель макроса должен либо превратить его в настоящую функцию, либо обернуть конструкцию в один оператор, либо использовать расширение gnu.
Шаблон do .. while(0)
- это подход "обернуть конструкцию".