7

get_cpu_var 宏定义如下

 29 #define get_cpu_var(var) (*({                           \
 30         extern int simple_identifier_##var(void);       \
 31         preempt_disable();                              \
 32         &__get_cpu_var(var); }))

似乎难以理解。我假设它是一种函数宏,它返回一个变量指针(基于星号)或者它是某种函数指针。我什至接近它吗?有人能启发我吗?

4

1 回答 1

16

您在开始({和结束之间看到的})语句表达式- 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;

并且您想编写一个表达式,该表达式将返回ab作为左值(假设我们要分配42给它),具体取决于选择器变量select。一个天真的尝试可能如下所示

(select ? a : b) = 42;

这是行不通的,因为在 C 语言中,?:运算符失去了其操作数的左值性。结果是一个无法赋值的右值。在这种情况下,*&诀窍就派上用场了

*(select ? &a : &b) = 42;

现在它按预期工作。

这正是原始海报的宏定义如何以及为什么包含一个看似多余的应用*and &。因此,您可以在分配的任一侧使用上述get_cpu_var

something = get_cpu_var(something);
get_cpu_var(something) = something;

如果没有这个技巧,您将只能get_cpu_var在右侧使用。

在 C++ 语言中,使用引用可以达到相同的效果。在 C 中我们没有引用,所以我们使用类似这样的技巧。

于 2012-04-15T21:35:21.920 回答