c - Linux内核源码中遇到的深奥#define宏

标签 c gcc linux-kernel c-preprocessor

定义如下的get_cpu_var宏

 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 语言中函数不能返回左值。

& 运算符产生一个指针(整个语句表达式的结果),然后由出现在上述宏开头的 * 运算符取消引用定义。所以,上面的宏本质上等同于 *&__get_cpu_var(var) 表达式。

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

这个技巧在任何方面都不限于 GCC 语句表达式。在普通的日常C程序设计中用的比较多。例如,假设您有两个变量

int a, b;

并且您想编写一个表达式,将 ab 作为左值返回(假设我们想将 42 分配给it) 取决于选择器变量 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++ 语言中,使用引用 可以达到同样的效果。在 C 语言中我们没有引用,所以我们改用这样的技巧。

关于c - Linux内核源码中遇到的深奥#define宏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10166275/

相关文章:

c++ - 解释需要 : log10 faster than log and log2, 但仅限于 O2 和更大

c - 告诉 gcc 专门展开一个循环

linux - 用于 Linux 的设备树感知 i2c gpio 驱动程序示例?

python - fd 从 python 到子进程的重复

linux - 在 Linux 或 Ubuntu 中,如何在没有任何外部工具的情况下获取硬件信息(确切的计算机型号、版本)?或者 lshw 如何在下面获取此信息?

c - rm命令中pattern的含义

c - 尝试清除输入缓冲区时无限循环

linux - 我如何知道我的 Linux 服务器上当前安装了哪个版本的 gcc?

c++ - 通过宏内联函数

c - 对于 switch 语句的情况,在局部 block 中定义变量是个好主意吗?