您在({ 和}) 之间看到的是一个语句表达式 - GCC 编译器的一项非标准功能,它允许将复合语句嵌入到 C 表达式中。这种语句表达式的结果是({}) 中的最后一个表达式语句。在您的情况下,这将是 &__get_cpu_var(var)。
& 运算符应用于__get_cpu_var(var) 子表达式的结果。这意味着 __get_cpu_var 返回一个左值。如果这确实是 C,那么 __get_cpu_var 也必须是一个宏,因为在 C 语言中函数不能返回左值。
& 运算符产生一个指针(整个语句表达式的结果),然后由上述宏定义开头的 * 运算符取消引用。所以,上面的宏本质上等价于*&__get_cpu_var(var) 表达式。
有些人可能会问为什么它被实现为*&__get_cpu_var(var) 而不仅仅是__get_cpu_var(var)。这样做是为了保留__get_cpu_var(var) 结果的左值性。语句表达式的结果始终是右值,即使 ({}) 中的最后一个语句是左值。为了保持结果的左值性,使用了众所周知的*& 技巧。
这个技巧不限于任何方式的 GCC 语句表达式。它在普通的日常 C 编程中相对经常使用。例如,假设您有两个变量
int a, b;
并且您想要编写一个表达式,该表达式将返回a 或b 作为左值(假设我们要为其分配42),具体取决于选择器变量select。一个天真的尝试可能如下所示
(select ? a : b) = 42;
这不起作用,因为在 C 语言中,?: 运算符失去了其操作数的左值性。结果是一个无法赋值的右值。在这种情况下,*& 技巧就派上用场了
*(select ? &a : &b) = 42;
现在它按预期工作了。
这正是原始发帖人的宏定义包含* 和& 的看似多余的应用的方式和原因。因此,您可以在分配的任一侧使用上述 get_cpu_var 宏
something = get_cpu_var(something);
get_cpu_var(something) = something;
如果没有这个技巧,您将只能在右侧使用 get_cpu_var。
在 C++ 语言中,使用 references 可以达到相同的效果。在 C 中我们没有引用,所以我们使用类似的技巧。