高级C问题:请解释C构造*({foo(& bar);& bar;}) [英] Advanced C question: Please explain C construct *({ foo(&bar); &bar; })

查看:116
本文介绍了高级C问题:请解释C构造*({foo(& bar);& bar;})的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这最终是一个C问题,是在Linux内核源代码的complete.h中学习代码时出现的,在那里我看到了以前从未在C中使用过的C技术.尽管对它的操作有一个模糊的了解,但我想通过精确的描述来微调我的理解,而且我不确定如何在不花很长时间的磨难的情况下用Google搜索答案.

This is ultimately a C question that arose when studying code in completion.h of the Linux kernel source, where I see a C technique I've never used in C before. Although have a vague sense what it's doing, I'd like to fine tune my understanding with a precise description, and I'm not quite sure how to search for the answer with Google without a potentially a long ordeal.

来自Linux内核的完成.h:

The relevant lines of code from the linux kernel's completion.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);
}

并在使用中:

int myFunc()
{
   DECLARE_COMPLETION_ON_STACK(comp);
   .
   .
   .
   wait_for_completion(&comp);
}

具体来说,我想了解代码 COMPLETION_INITIALIZER_ON_STACK.

Specifically, I want to understand the code of COMPLETION_INITIALIZER_ON_STACK.

我相信两个语句的支撑主体{ init_completion(&work); &work; }只会产生一个值&work(NOP语句),根据我对C中支撑块的了解,它会得出最后一个赋值的值大小写,即结构的地址.

I believe the braced body of two statements { init_completion(&work); &work; } results in simply a value, &work (a NOP statement), which from what I know about braced blocks in C, yeilds the value of the last assignment, in this case, the address of a struct.

但是有趣的是(*( ))中所有内容的包围(以及令我感到困惑的地方).

But it is the enclosing of all of that in *( ) that gets interesting (and where I am bewildered).

  1. 提取"到底是在做什么??
  2. 是否导致功能init_completion()被调用(可能)?
  3. 指向作为获取的对象的结构的指针的结果是什么?
  4. 可以在什么情况下使用它?
  1. What is that 'fetch' doing exactly?
  2. Is it resulting in the function init_completion() being invoked (probably)?
  3. And what is the result of a pointer to a struct as a fetched object?
  4. In what contexts can it be applied?

我不确定正在发生什么,如何构思它,以及如何像DECLARE_COMPLETION_ON_STACK中那样将结果分配给struct completion work.

I'm not sure what is happening, how to conceive of it, and how it is it possible to assign that result to struct completion work as is done in in DECLARE_COMPLETION_ON_STACK.

任何对此的教育将不胜感激.

Any education about this would be appreciated.

推荐答案

({ ... })块中语句的语法是 statement表达式,它是GCC扩展.它允许您运行一系列语句,其中块中的最后一条语句是一个表达式,该表达式成为完整语句表达式的值.因此,在这种情况下,语句表达式的值为&work.

The syntax of statements within a ({ ... }) block is a statement expression which is a GCC extension. It allows you to run a series of statements where the last statement in the block is an expression which becomes the value of the full statement expression. So in this case the statement expression has the value &work.

由于语句表达式的计算结果为&work,因此在语句表达式之前的*给您*&work或等效为work的宏COMPLETION_INITIALIZER_ONSTACK值.

Since the statement expression evaluates to &work, the * right before the statement expression gives you *&work, or equivalently work as the value of the macro COMPLETION_INITIALIZER_ONSTACK.

现在让我们看一下DECLARE_COMPLETION_ONSTACK.使用时:

Now let's look at DECLARE_COMPLETION_ONSTACK. When it is used:

DECLARE_COMPLETION_ON_STACK(comp);

它扩展为:

struct completion comp= COMPLETION_INITIALIZER_ONSTACK(comp);

其中进一步扩展到:

struct completion comp = (*({ init_completion(&comp ); ∁ }))

首先,使用语句表达式初始化变量comp.该表达式中的第一条语句是对函数init_completion的调用,该函数传递给新变量的地址.此函数设置变量的值,此变量尚未实际初始化.语句表达式中的下一个(也是最后一个)语句是&comp,它是语句表达式的值.然后,该地址被取消引用,从而为我们提供comp,然后将其分配给comp.因此,该变量已使用自身进行了有效的初始化!

Breaking this down, the variable comp is being initialized with a statement expression. The first statement in that expression is a call to the function init_completion which is passed the address of the new variable. This function sets the values of the variable which at this point hasn't actually been initialized yet. The next (and last) statement in the statement expression is &comp which is the value of the statement expression. This address is then dereferenced giving us comp which is then assigned to comp. So the variable is being validly initialized with itself!

通常使用自身初始化变量会调用未定义的行为,因为您将尝试读取未初始化的变量,但在这种情况下不会这样做,因为变量的地址会传递给一个函数,该函数会在初始化之前为其字段分配值.

Normally initializing a variable with itself would invoke undefined behavior because you would be trying to read an uninitialized variable, but not in this case because the variable's address is passed to a function which assigns values to its fields before it's initialized.

您可能会问为什么未如此定义COMPLETION_INITIALIZER_ONSTACK:

You might ask why COMPLETION_INITIALIZER_ONSTACK was not defined like this:

#define COMPLETION_INITIALIZER_ONSTACK(work) \
    ({ init_completion(&work); work; })

如果采用这种方式,则会在堆栈上创建一个临时变量.使用地址会阻止这种情况的发生.实际上,代码最初是这样做的,但已更改为您在以下提交中看到的内容:

If done this way, a temporary variable is created on the stack. Using the addrress prevents this from happening. In fact the code originally did this but was changed to what you see in the following commit:

https://github.com/torvalds/linux/commit/ec810480340 #diff-f4f6d7a50d07f6f07835787ec35565bb

这篇关于高级C问题:请解释C构造*({foo(& bar);& bar;})的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆