在Linux内核源代码中遇到Abstruse #define宏

get_cpu_var marcro,定义如下

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

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

你在开头({和结束})之间看到的是一个语句表达式 – GCC编译器的一个非标准特性,它允许将复合语句嵌入到C表达式中。 这种语句表达式的结果是({})最后一个表达式语句。 在你的情况下,将是&__get_cpu_var(var)

&运算符应用于__get_cpu_var(var)子表达式的结果。 这意味着__get_cpu_var返回一个左值。 如果这确实是C,那么__get_cpu_var也必须是一个宏,因为在C语言中函数不能返回lvalues。

&运算符生成一个指针(整个语句表达式的结果),然后由上面宏定义开头的*运算符取消引用。 因此,上面的宏基本上等同于*&__get_cpu_var(var)表达式。

有些人可能会问为什么它实现为*&__get_cpu_var(var)而不仅仅是__get_cpu_var(var) 。 这样做是为了保留__get_cpu_var(var)结果的__get_cpu_var(var) 。 语句表达式的结果总是rvalue,即使({})的最后一个stetement是左值。 为了保持结果的左值,使用众所周知的*&技巧。

这个技巧不仅限于GCC语句表达式。 它通常用于普通的日常C编程。 例如,假设您有两个变量

 int a, b; 

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

 (select ? a : b) = 42; 

这不起作用,因为在C语言中, ?:运算符会丢失其操作数的左值。 结果是rvalue,无法分配。 在这种情况下, *&技巧来拯救

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

现在它按预期工作。

这正是原始海报的宏定义包含*&的看似冗余应用的方式和原因。 因此,您可以在get_cpu_var任何一侧使用上面的get_cpu_var

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

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

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