来自内核代码的这个宏中##的含义是什么

Jee*_*tel 4 c macros linux-kernel

我在一个内核代码中找到了这个宏.. http://lxr.free-electrons.com/source/arch/alpha/include/asm/io.h?v=3.0;a=arm#L140

#define IO_CONCAT(a,b)  _IO_CONCAT(a,b)
#define _IO_CONCAT(a,b) a ## _ ## b
Run Code Online (Sandbox Code Playgroud)

我无法理解这个含义.有人知道吗?

编辑:

那么它将返回什么

  return IO_CONCAT(__IO_PREFIX,readl)(addr);
Run Code Online (Sandbox Code Playgroud)

Eld*_*mov 8

双哈希用于将两个标记连接在一起:

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

CONCAT(x, y) # Gives 'xy'
Run Code Online (Sandbox Code Playgroud)

但是,如果传递的参数之一是宏本身,这种天真的实现不起作用:

#define Z y
CONCAT(x, Z) # Gives 'xZ', not 'xy' as one might expect
Run Code Online (Sandbox Code Playgroud)

这就是你的问题中使用宏间接的原因:

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

#define Z y
CONCAT(x, Z) # Gives 'xy' 
Run Code Online (Sandbox Code Playgroud)

UPD.

现在考虑一下你要问的具体例子:

return IO_CONCAT(__IO_PREFIX,readl)(addr);
Run Code Online (Sandbox Code Playgroud)

__IO_PREFIX显然是一个宏(Linux内核中的大写标识符通常是宏).它定义在几个地方,其中之一是:

#define __IO_PREFIX             generic
Run Code Online (Sandbox Code Playgroud)

现在让我们看看采取了哪些步骤来扩展原始声明:

  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);
    


Mat*_*Mat 5

这是一种象征性的粘贴.它将令牌连接在一起.所以IO_CONCAT(foo,bar)会扩展到foo_bar.

它在C99的§6.10.3.3中定义:

如果在类函数宏的替换列表中,参数紧跟在##预处理标记之前或之后,则该参数将被相应参数的预处理标记序列替换; 但是,如果参数不包含预处理标记,则参数将替换为地标标记预处理标记.)

对于类似对象和类似函数的宏调用,在重新检查替换列表以替换更多宏名称之前,##删除替换列表中的预处理标记的每个实例(不是来自参数),并且连接前面的预处理标记使用以下预处理标记.专门处理地标预处理令牌:两个地方标记的连接导致单个地标标记预处理标记,并且地标标记与非地标标记预处理标记的连接导致非地标标记预处理标记.如果结果不是有效的预处理标记,则行为未定义.生成的令牌可用于进一步的宏替换.##运营商的评估顺序未指定.


per*_*eal 5

这是预处理程序令牌连接运算符:

##' preprocessing operator performs token pasting. When a macro is expanded, the two tokens on either side of each##"操作符被组合成一个单一的令牌,该令牌然后替换`##"和在宏扩展两个原始标记.通常两者都是标识符,或者一个是标识符,另一个是预处理号.粘贴后,它们会生成更长的标识符.这不是唯一有效的案例.也可以将两个数字(或数字和名称,例如1.5和e3)连接成一个数字.此外,可以通过令牌粘贴形成诸如+ =的多字符运算符.