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

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

问题描述

这最终是一个 C 问题,是在研究 Linux 内核源代码的 completion.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.

我相信两个语句的支撑体 { init_completion(&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. 它可以应用于哪些环境?

我不确定发生了什么,如何构思它,以及如何像在 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.

推荐答案

({ ... }) 块内的语句语法是一个语句表达式是 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/ec81048cc340bb03334e6ca62661ecc0a684897a#diff-f4f6d7a50d07f6f07835787ec35565bb

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

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