cle*_*ght 5 c gcc struct initializer linux-kernel
最终,这是在Linux内核源代码的complete.h中学习代码时出现的一个C问题,在那里我看到了以前从未在C中使用过的C技术。尽管对它的操作有一个模糊的感觉,但我想通过精确的描述来微调我的理解,而且我不确定如何在不费劲的情况下用Google搜索答案。
来自Linux内核的complete.h的相关代码行:
struct completion {
unsigned int done;
wait_queue_head_t wait;
};
#define COMPLETION_INITIALIZER_ONSTACK(work) \
(*({ init_completion(&work); &work; }))
#define DECLARE_COMPLETION_ONSTACK(work) \
struct completion work = COMPLETION_INITIALIZER_ONSTACK(work)
static inline void init_completion(struct completion *x)
{
x->done = 0;
init_waitqueue_head(&x->wait);
}
Run Code Online (Sandbox Code Playgroud)
并在使用中:
int myFunc()
{
DECLARE_COMPLETION_ON_STACK(comp);
.
.
.
wait_for_completion(&comp);
}
Run Code Online (Sandbox Code Playgroud)
具体来说,我想了解的代码COMPLETION_INITIALIZER_ON_STACK。
我相信两个语句的括号主体{ init_completion(&work); &work; }仅产生一个值&work(NOP语句),根据我对C中的括号块的了解,该值将得出最后一个赋值的值,在这种情况下为结构的地址。
但是,所有这些内容的封闭*( )变得很有趣(这让我感到困惑)。
init_completion()被调用(可能)?我不确定发生了什么,如何构思它以及如何将结果分配给struct completion workin中完成的不确定DECLARE_COMPLETION_ON_STACK。
任何对此的教育将不胜感激。
({ ... })块中语句的语法是GCC扩展的语句表达式。它允许您运行一系列语句,其中块中的最后一条语句是一个表达式,该表达式成为完整语句表达式的值。因此,在这种情况下,语句表达式具有值&work。
由于语句表达式的计算结果为&work,因此*语句表达式之前的内容即为您*&work,或者等效work为宏的值COMPLETION_INITIALIZER_ONSTACK。
现在让我们看一下DECLARE_COMPLETION_ONSTACK。使用时:
DECLARE_COMPLETION_ON_STACK(comp);
Run Code Online (Sandbox Code Playgroud)
它扩展为:
struct completion comp= COMPLETION_INITIALIZER_ONSTACK(comp);
Run Code Online (Sandbox Code Playgroud)
进一步扩展为:
struct completion comp = (*({ init_completion(&comp ); ∁ }))
Run Code Online (Sandbox Code Playgroud)
分解来看,该变量comp正在使用语句表达式进行初始化。该表达式中的第一条语句是对函数的调用,该函数init_completion传递给新变量的地址。此函数设置变量的值,此变量尚未实际初始化。语句表达式中的下一个(也是最后一个)语句是语句表达式&comp的值。然后,该地址被取消引用comp,然后分配给我们comp。因此,该变量将使用自身进行有效初始化!
通常,用自身初始化变量会调用未定义的行为,因为您将尝试读取未初始化的变量,但在这种情况下不会这样做,因为变量的地址会传递给一个函数,该函数会在初始化之前为其字段分配值。
您可能会问为什么COMPLETION_INITIALIZER_ONSTACK没有这样定义:
#define COMPLETION_INITIALIZER_ONSTACK(work) \
({ init_completion(&work); work; })
Run Code Online (Sandbox Code Playgroud)
如果这样做,将在堆栈上创建一个临时变量。使用地址会阻止这种情况的发生。实际上,代码最初是这样做的,但是更改为您在以下提交中看到的内容: