linux 内核中有争议的 __set_task_state(tsk, state_value) 宏语法
Controversial __set_task_state(tsk, state_value) macro syntax in linux kernel
我在 linux kernel 2.4.31
文件中的几个宏中遇到过这种语法,但为了突出重点,我询问的是 include/linux/sched.h
中定义的 __set_task_state(tsk, state_value)
宏:
#define __set_task_state(tsk, state_value) \
do { (tsk)->state = (state_value); } while (0)
用 do while(0)
语句定义上述宏的“优势”是什么?有什么副作用吗?
像这样定义它更方便:
#define __set_task_state(tsk, state_value) (tsk)->state = (state_value)
因为这个宏扩展到单行所以没有多大意义,但是有一个副作用,使用 do ...
版本使得表达式不能在两个方向上赋值:
state = __set_task_state(tsk, state_value); // syntax error in the do version
__set_task_state(tsk, state_value) = state; // syntax error in the do version
使用 do-while
而不是不使用它来定义它至少有两个原因。
首先,如果您在定义时不使用 do-while
,那么宏可以用作 returns 值的函数。如果你不喜欢,你应该使用 do-while
包装器,即使是单语句宏。
第二个原因类似。如果您在没有包装器的情况下定义它,那么您可以使用它来赋值:
set_task_state(my_task, state) = different_state;
这将是完全合法的,但对于阅读您的代码的任何人来说都非常混乱。它之所以有效,是因为它扩展为:
(my_task)->state = (state) = different_state;
另一个原因是 if-else 语句。如果你像这样使用它而没有宏中的 do-while(0):
if (check)
__set_task_state(tsk, state_value);
else
do_something_else;
编译器会因为没有关联的“if”的“else”(例如孤立的“else”)而报错,因为宏扩展会将上面的代码翻译为“if”下没有大括号的两条指令:
if (check)
(tsk)->state = (state_value);; <-- The double ";;" separates two instructions under the "if".
<-- This makes the following "else" orphan
<-- This requires to add braces to fix the compilation error
else
do_something_else;
宏中的 do-while(0) 使分组指令显示为单个指令。这里的扩展是:
if (check)
do { (tsk)->state = (state_value); } while(0); <-- Here, there is a single instruction under the "if"
<-- So the following "else" is syntactically correct
<-- No need to add braces
else
do_something_else;
N.B.:此技巧通常用于将多个指令组合在一个宏中,使它们显示为单个指令:
#define MACRO() do { inst1; inst2; inst3;...; } while (0)
这使得以下工作无需大括号:
if (check)
MACRO();
else
do_something_else;
这也避免了严重的隐藏错误,例如:
while(check)
MACRO();
如果没有 do-while(0) 构造,只有第一条指令(即 inst1)会在 while(检查)循环!
在这个link, there are additional tips to avoid traps with the c-preprocessor. The above tip is described in §3.
我在 linux kernel 2.4.31
文件中的几个宏中遇到过这种语法,但为了突出重点,我询问的是 include/linux/sched.h
中定义的 __set_task_state(tsk, state_value)
宏:
#define __set_task_state(tsk, state_value) \
do { (tsk)->state = (state_value); } while (0)
用 do while(0)
语句定义上述宏的“优势”是什么?有什么副作用吗?
像这样定义它更方便:
#define __set_task_state(tsk, state_value) (tsk)->state = (state_value)
因为这个宏扩展到单行所以没有多大意义,但是有一个副作用,使用 do ...
版本使得表达式不能在两个方向上赋值:
state = __set_task_state(tsk, state_value); // syntax error in the do version
__set_task_state(tsk, state_value) = state; // syntax error in the do version
使用 do-while
而不是不使用它来定义它至少有两个原因。
首先,如果您在定义时不使用 do-while
,那么宏可以用作 returns 值的函数。如果你不喜欢,你应该使用 do-while
包装器,即使是单语句宏。
第二个原因类似。如果您在没有包装器的情况下定义它,那么您可以使用它来赋值:
set_task_state(my_task, state) = different_state;
这将是完全合法的,但对于阅读您的代码的任何人来说都非常混乱。它之所以有效,是因为它扩展为:
(my_task)->state = (state) = different_state;
另一个原因是 if-else 语句。如果你像这样使用它而没有宏中的 do-while(0):
if (check)
__set_task_state(tsk, state_value);
else
do_something_else;
编译器会因为没有关联的“if”的“else”(例如孤立的“else”)而报错,因为宏扩展会将上面的代码翻译为“if”下没有大括号的两条指令:
if (check)
(tsk)->state = (state_value);; <-- The double ";;" separates two instructions under the "if".
<-- This makes the following "else" orphan
<-- This requires to add braces to fix the compilation error
else
do_something_else;
宏中的 do-while(0) 使分组指令显示为单个指令。这里的扩展是:
if (check)
do { (tsk)->state = (state_value); } while(0); <-- Here, there is a single instruction under the "if"
<-- So the following "else" is syntactically correct
<-- No need to add braces
else
do_something_else;
N.B.:此技巧通常用于将多个指令组合在一个宏中,使它们显示为单个指令:
#define MACRO() do { inst1; inst2; inst3;...; } while (0)
这使得以下工作无需大括号:
if (check)
MACRO();
else
do_something_else;
这也避免了严重的隐藏错误,例如:
while(check)
MACRO();
如果没有 do-while(0) 构造,只有第一条指令(即 inst1)会在 while(检查)循环!
在这个link, there are additional tips to avoid traps with the c-preprocessor. The above tip is described in §3.