【发布时间】:2012-12-05 23:30:12
【问题描述】:
在 Linux 的多个 ARM 后端,我在文件 clkdev.h 中看到了这个宏定义:
#define __clk_get(clk) ({ 1; })
参见例如./arch/arm/mach-versatile/include/mach/clkdev.h
此宏正在使用 GCC 扩展 Statements and Declarations in Expressions
后来这个宏被用在文件./drivers/clk/clkdev.c,函数clk_get_sys()中
if (cl && !__clk_get(cl->clk))
cl = NULL;
我想知道为什么不在这里使用一个简单的宏:
#define __clk_get(clk) (1)
编辑:
我使用以下 grep 模式在整个内核源代码中发现了此构造的一些其他用法:
grep -R '({[[:space:]]*[a-zA-Z0-9_()+=/!&*>., ?:-]\+[[:space:]]*;[[:space:]]*})' .
以下是部分比赛:
./kernel/trace/trace_selftest.c:# define trace_selftest_startup_dynamic_tracing(trace, tr, func) ({ 0; })
./kernel/profile.c:#define create_hash_tables() ({ 0; })
./include/asm-generic/bug.h: * Use of ({0;}) because WARN_ON_SMP(x) may be used either as
./include/asm-generic/bug.h:# define WARN_ON_SMP(x) ({0;})
./include/linux/key.h:#define key_get(k) ({ NULL; })
./include/linux/key.h:#define key_get(k) ({ NULL; })
./include/linux/audit.h:#define audit_alloc(t) ({ 0; })
./include/linux/audit.h:#define audit_bprm(p) ({ 0; })
./include/linux/audit.h:#define audit_sockaddr(len, addr) ({ 0; })
./include/linux/audit.h:#define audit_log_bprm_fcaps(b, ncr, ocr) ({ 0; })
./include/linux/audit.h:#define audit_log_start(c,g,t) ({ NULL; })
./include/linux/atalk.h:#define atalk_proc_init() ({ 0; })
./include/linux/ftrace.h:#define register_ftrace_function(ops) ({ 0; })
./include/linux/ftrace.h:#define unregister_ftrace_function(ops) ({ 0; })
./include/linux/ftrace.h:#define ftrace_regex_open(ops, flag, inod, file) ({ -ENODEV; })
./include/linux/ftrace.h:#define ftrace_set_filter(ops, buf, len, reset) ({ -ENODEV; })
./include/linux/ftrace.h:#define ftrace_set_notrace(ops, buf, len, reset) ({ -ENODEV; })
./include/linux/cpu.h:#define unregister_hotcpu_notifier(nb) ({ (void)(nb); })
./include/linux/proc_fs.h:#define proc_net_fops_create(net, name, mode, fops) ({ (void)(mode), NULL; })
./arch/powerpc/include/asm/pgtable-ppc64.h:#define pgd_set(pgdp, pudp) ({pgd_val(*(pgdp)) = (unsigned long)(pudp);})
./arch/sh/math-emu/math.c:#define WRITE(d,a) ({if(put_user(d, (typeof (d)*)a)) return -EFAULT;})
./arch/sh/math-emu/math.c:#define READ(d,a) ({if(get_user(d, (typeof (d)*)a)) return -EFAULT;})
[...]
注意:构造 ({if(put_user(d, (typeof (d)*)a)) return -EFAULT;}) 似乎是复合语句的一个很好的用法。不过这个也可以换成更典型的do { if(put_user(d, (typeof (d)*)a)) return -EFAULT; } while(0)
grep 返回的一个匹配很有趣:在./include/asm-generic/bug.h 中有一个({ 0; }) 用法的评论。这与AndreyT 的answer 完全相同。
确实,不能使用((void)0),因为它不能用作右值。
({ 0; }) 在每种情况下都有效。
因此,如果您有一个可以像函数一样使用的宏,它返回一个可以使用或不可以使用的值,复合语句似乎是您唯一的选择。
但__clkget() 从未用作其他任何东西作为 r 值
一些链接
- http://www.toofishes.net/blog/gcc-compound-statement-expressions/
- Abstruse #define macro encountered in Linux kernel source
- macro: does #define a(b) ({... c;}) means a(b) returns c?
- Why do we need parentheses around block macro?
- Are compound statements (blocks) surrounded by parens expressions in ANSI C?
【问题讨论】:
-
我的猜测是宏曾经有一些肉,并且在某些时候有人破解它总是评估为 1 而没有删除 GCC 扩展括号语法。
-
不,现在我认为 GCC 扩展括号语法可能会破坏“条件表达式始终为真”的警告?
-
@phonetagger 这是 gcc 警告吗?但我确实看到可能会出现 MSVC C4127 警告。
-
@JosephQuinsey - 我自己没有收到这样的警告,但是和我一起工作的人一直在调整其他人签到的代码,因为他自己的 linux 机器上的 GCC 版本更新了很多比其他人在服务器上使用的版本,其他人的正常编码实践显然在他使用的任何版本上都不能很好地工作(就警告而言)。我不知道他用的是什么版本。
-
@H2CO3 我认为没有人可以使用任何 Microsoft 产品编译 vanilla Linux 内核。
标签: c linux gcc macros linux-kernel