要研究LINUX内核,C语言是基础中的基础,但是LINUX并不是完全的标准C,而是对标准C做了很多扩展,这些扩展特性对于我们分析内核有着很重要的作用,下面做些总结性的工作。
3 变参宏
标准C的变参宏
- 在ISO C99里,一个宏可以被声明为带可变的参数个数,就像函数一样。语法如下:
- 这里的 “…” 代表变参,在引用宏debug的地方它代表着零个或多个相应的标识符,包括逗号。这些标识符将会替换
__VA_ARGS__
。
但是这样的宏不能处理零变参的情况,否则编译不会通过,因为零变参的时候会多一个逗号。
GCC的变参宏
- GCC 支持变参宏,并且提供另一种词法来定义它,即可赋予变参名称,就像普通参数一样:
- 这种用法与上面所述的ISO C形式的宏定义完全一样,只是看起来更具阅读性。
args跟后面的三个点可以连在一起,也可以用空格分开,当然这个宏同样不支持零变参的情形,原因同上。
GNU C的变参宏
- 除了前面提到的可以为变参命名之外,GNU 预处理器CPP对ISO C的变参宏还进行了进一步的扩展,使之能处理零变参个数的情况。
- 举例来说,以下这个语句在ISO C编译器中编译时是错的:
debug(“A message”)
。在ISO C中不允许省略所有的变参,因为在这个字符串之后缺少了一个逗号”,”。 - GNU预处理器CPP允许你省略全部的变参,方法是在变参前加上黏贴符“##”:
或者
这样,当我们省略变参的时候黏贴符能自动清除前面多余的逗号。
- 另外,在宏里面,除了两个井号 ## 可以作为黏贴符之外,其实一个井号 # 也可以用来黏贴符号,但是它要被用在字符串当中,例如:
执行的结果如下:
- 在字符串中,我们可以用一个井号来黏贴宏参数,就像上面我们看到的那样。其中字符串与黏贴字符之间的空格是可选的,预处理器会自动去掉多余的空格。
总结:在上面的例子中:
- 故意在四个地方都用到了标识符n(那个转义换行符'/n'不在讨论范围内),依次分析是:
1.在字符串中直接出现的“宏参数”实际上并不会被当成参数,而是一个普通的字符n;
2.如果要解决第一个问题,那就要在字符串当中使用一个井号 # 来黏贴宏参数;
3.不在字符串当中,要黏贴宏参数,则需要两个井号 ## 来黏贴;
4.不在字符串中,如果直接出现宏参数,则预处理器将进行宏展开。