![6217760-e609897bd82c4891.png](https://i-blog.csdnimg.cn/blog_migrate/90079f471168a43637ed4fa805505b95.png)
要研究LINUX内核,C语言是基础中的基础,但是LINUX并不是完全的标准C,而是对标准C做了很多扩展,这些扩展特性对于我们分析内核有着很重要的作用,下面做些总结性的工作。
3 变参宏
标准C的变参宏
- 在ISO C99里,一个宏可以被声明为带可变的参数个数,就像函数一样。语法如下:
#define debug(format, …) fprintf(stderr, format, __VA_ARGS__)
- 这里的 “…” 代表变参,在引用宏debug的地方它代表着零个或多个相应的标识符,包括逗号。这些标识符将会替换
__VA_ARGS__
。
但是这样的宏不能处理零变参的情况,否则编译不会通过,因为零变参的时候会多一个逗号。
GCC的变参宏
- GCC 支持变参宏,并且提供另一种词法来定义它,即可赋予变参名称,就像普通参数一样:
#define debug(format, args…) fprintf(stderr, format, args)
- 这种用法与上面所述的ISO C形式的宏定义完全一样,只是看起来更具阅读性。
args跟后面的三个点可以连在一起,也可以用空格分开,当然这个宏同样不支持零变参的情形,原因同上。
GNU C的变参宏
- 除了前面提到的可以为变参命名之外,GNU 预处理器CPP对ISO C的变参宏还进行了进一步的扩展,使之能处理零变参个数的情况。
- 举例来说,以下这个语句在ISO C编译器中编译时是错的:
debug(“A message”)
。在ISO C中不允许省略所有的变参,因为在这个字符串之后缺少了一个逗号”,”。 - GNU预处理器CPP允许你省略全部的变参,方法是在变参前加上黏贴符“##”:
#define debug(format,…) fprintf(stderr, format, ##__VA_ARGS__)
或者
#define debug(format, args…) fprintf(stderr, format, ##args)
这样,当我们省略变参的时候黏贴符能自动清除前面多余的逗号。
- 另外,在宏里面,除了两个井号 ## 可以作为黏贴符之外,其实一个井号 # 也可以用来黏贴符号,但是它要被用在字符串当中,例如:
#define prt(n) printf("calculate i'n: " "i#n " = %d, with parameter %d/n", i##n, n)
int i = 1;
int i8 = 800, i9 = 900;
prt(8); prt(9);
执行的结果如下:
calculate i'n: i8 = 800, with parameter 8
calculate i'n: i9 = 900, with parameter 9
- 在字符串中,我们可以用一个井号来黏贴宏参数,就像上面我们看到的那样。其中字符串与黏贴字符之间的空格是可选的,预处理器会自动去掉多余的空格。
总结:在上面的例子中:
#define prt(n) printf("calculate i'n: " "i#n " = %d\n", i##n)
- 故意在四个地方都用到了标识符n(那个转义换行符'/n'不在讨论范围内),依次分析是:
1. 在字符串中直接出现的“宏参数”实际上并不会被当成参数,而是一个普通的字符n;
2. 如果要解决第一个问题,那就要在字符串当中使用一个井号 # 来黏贴宏参数;
3. 不在字符串当中,要黏贴宏参数,则需要两个井号 ## 来黏贴;
4. 不在字符串中,如果直接出现宏参数,则预处理器将进行宏展开。
===========我是华丽的分割线===========
更多知识:
点击关注专题:嵌入式Linux&ARM
或浏览器打开:嵌入式 Linux C ARM - 专题 - 简书
或扫描二维码:
![6217760-e6bba06e005d8fe7.jpg](https://i-blog.csdnimg.cn/blog_migrate/a1ec4e193502a058c4c76e0d855f1d69.jpeg)