这个内核代码宏中的##代表什么意思?

5

请在您最喜欢的C语言书籍中查看“token pasting operator”。 - Hans Passant
3个回答

10

双#号用于将两个标记连接在一起:

#define CONCAT(a,b) a ## b

CONCAT(x, y) # Gives 'xy'

然而,这种天真的实现在某些参数本身是宏的情况下不起作用:

#define Z y
CONCAT(x, Z) # Gives 'xZ', not 'xy' as one might expect

这就是为什么你的问题中使用了宏间接引用:

#define CONCAT(a,b)  __CONCAT(a,b)
#define __CONCAT(a,b) a ## b

#define Z y
CONCAT(x, Z) # Gives 'xy' 

更新。

现在考虑你所询问的具体示例:

return IO_CONCAT(__IO_PREFIX,readl)(addr);

这里__IO_PREFIX显然是一个宏定义(Linux内核中的大写标识符通常是宏定义)。它在几个位置中被定义,其中之一是:

#define __IO_PREFIX             generic

现在我们来看看对原始语句进行扩展所采取的步骤:

  1. 扩展__IO_PREFIX
    return IO_CONCAT(generic,readl)(addr);
    
  2. 扩展IO_CONCAT(...)
    return _IO_CONCAT(generic,readl)(addr);
    
  3. 扩展_IO_CONCAT(...)
    return generic_readl(addr);
    

根据您的回答,我编写了这段代码http://codepad.org/MgdFuRIq,为什么它不起作用? - Jeegar Patel
1
@Mr.32因为10_20在C语言中不是一个有效的标记符号...它既不是数字,也不是标识符。 - Eldar Abusalimov
嘿,感谢您的更新,现在我明白了整个事情...非常感谢......' - Jeegar Patel

5

那就是令牌粘贴。它将令牌连接在一起。所以IO_CONCAT(foo,bar)会扩展为foo_bar

它在C99的§6.10.3.3中定义:

如果在函数式宏的替换列表中,一个参数紧接着前后跟随着一个“##”预处理标记,那么这个参数将被相应的实参的预处理标记序列所替换;但是,如果一个实参不包含任何预处理标记,则该参数将被一个占位符预处理标记所替换。对于对象式和函数式宏调用,在重新检查替换列表以查找更多要替换的宏名称之前,将删除替换列表中每个“##”预处理标记(不来自实参)的每个实例,并将前面的预处理标记与后面的预处理标记连接起来。占位符预处理标记会被特殊处理:两个占位符的连接将产生一个单一的占位符预处理标记,占位符与非占位符预处理标记的连接将产生非占位符预处理标记。如果结果不是有效的预处理标记,则行为是未定义的。生成的标记可用于进一步的宏替换。对于“##”运算符的评估顺序是未指定的。

5
这是预处理器的标记连接运算符
预处理器中的## 运算符用于实现标识符拼接。当宏被展开时,每个## 运算符左右两边的标记将被合并为一个单独的标记,然后取代宏展开中的`##'和原始的两个标记。通常情况下,这两个标记都是标识符,或者一个是标识符,另一个是预处理数字。当它们被拼接在一起时,它们会形成一个更长的标识符。这不是唯一有效的情况。也可以将两个数字(或数字和名称,如1.5和e3)连接成一个数字。此外,还可以通过标记拼接来形成多字符操作符,例如+=。

这是标准的C语言,不是GNU。 (GCC有一些对##的扩展,但此宏未使用它们。) - zwol

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接