在Linux内核代码中,有非常多的代码使用了GCC的很多扩展特性,本篇文章主要是对这些特性做一个小小的总结,代码全部来自于GCC手册
1.被一对花括号包起来的代码块可以作为一个表达式来使用,花括号中可以使用循环语句(while、for),开关语句(if、switch),本地变量等。代码如下:
1 2 3 4 | ({ int y = foo (); int z; if (y > 0) z = y; else z = - y; z; }) |
最后一条语句必须为一个表达式并以分号结束,该表达式既为该代码块的返回值用在其他语句或表达式中
1 2 | #define maxint(a,b) \ ({ int _a = (a), _b = (b); _a > _b ? _a : _b; }) |
这样的宏定义比下面的代码在安全性上有很大的提高
1 | #define max(a,b) ((a) > (b) ? (a) : (b)) |
2.申明本地标签(local label)
GCC允许在一个代码块中申明任何本地标签,但只能在申明的代码块中使用,具体语法如下
1 | __label__ label1, label2, /* . . . */ ; |
该语句仅仅申明了两个标签名,但没有定义标签本身,还需要使用标签名后接冒号(label:)的方式定义标签,本地标签一般在复杂的宏定义中使用比较有效,如果一个宏定义在一个函数中被多次展开(被多次引用),那么一个普通的标签就会出现重复定义的情况,而本地标签就能很好的避免该问题,例如如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #define SEARCH(array, target) \ ({ \ __label__ found; \ typeof (target) _SEARCH_target = (target); \ typeof (*(array)) *_SEARCH_array = (array); \ int i, j; \ int value; \ for (i = 0; i < max; i++) \ for (j = 0; j < max; j++) \ if (_SEARCH_array[i][j] == _SEARCH_target) \ { value = i; goto found; } \ value = -1; \ found: \ value; \ }) |
3.获取标签的值
可以获取一个标签的值(地址),标签的值得类型为void *,该值得类型为常量,可以用在任何需要改类型的地方,具体使用语法如下:
1 2 3 4 | void *ptr; /* . . . */ ptr = &&foo;//foo为一个标签 goto *ptr; |
还可以这样用:
1 2 3 | static const int array[] = { &&foo - &&foo, &&bar - &&foo, &&hack - &&foo }; goto *(&&foo + array[i]); |
4.内嵌函数
内嵌函数可以在它定义的地方访问任何可见的变量,我们暂且称包含内嵌函数的函数为containing_function,如果我们在内嵌函数中我们访问了containing_function的局部变量,那么我们在containing_function外部调用内嵌函数极有可能会引起混乱,后果如法预料。反之无论如何我们在containing_function内部调用该内嵌函数都是安全的,示例代码如下:
1 2 3 4 5 6 | hack ( int *array, int size) { void store ( int index, int value) { array[index] = value; } intermediate (store, size); } |
还可以在内嵌函数中使用在containing_function中定义的标签:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | bar ( int *array, int offset, int size) { __label__ failure; int access ( int *array, int index) { if (index > size) goto failure; return array[index + offset]; } int i; /* . . . */ for (i = 0; i < size; i++) /* . . . */ access (array, i) /* . . . */ /* . . . */ return 0; /* Control comes here from access if it detects an error. */ failure: return -1; } |
5.构造函数
在GCC编译器中有很多内置函数这些函数都已__buildin_开始,具体就不一一介绍。
6.typeof
如果我们需要一个类型,我们可以使用typeof,该操作符返回一个变量的类型。如果想要是你的程序兼容IOS C程序,那么可以使用__typeof__,示例代码如下:
1 2 3 4 | #define max(a,b) \ ({ typeof (a) _a = (a); \ typeof (b) _b = (b); \ _a > _b ? _a : _b; }) |
更多用法如下:
1 2 3 4 5 6 7 | typeof (*x) y; typeof (*x) y; typeof (typeof ( char *)[4]) y; //等同于char *y[4]; #define pointer(T) typeof(T *) #define array(T, N) typeof(T [N]) array (pointer ( char ), 4) y; |
在GNU C++中可以使用__auto_type
7.在条件表达式中省略操作数
如果在条件表达式(? :)中省略第一个操作数,那么如果条件为真那么该表达式的值既为条件的值,如下:
1 | x ? : y//如果x不为0,那么该表达式的值为x |
8.零长数组
零长数组在结构体的最后一个成员中非常有用,如下:
1 2 3 4 5 6 7 | struct f1 { int x; int y[]; } f1 = { 1, { 2, 3, 4 } }; struct f2 { struct f1 f1; int data[3]; } f2 = { { 1 }, { 2, 3, 4 } }; |
1 2 3 4 5 6 | struct foo { int x; int y[]; }; struct bar { struct foo z; }; struct foo a = { 1, { 2, 3, 4 } }; //合法 struct bar b = { { 1, { 2, 3, 4 } } }; //非法 struct bar c = { { 1, { } } }; //合法 struct foo d[1] = { { 1, { 2, 3, 4 } } }; //非法 |
9.不固定的初始化
自动变量的集合初始化不必需要常量表达式:
1 2 3 4 5 | foo ( float f, float g) { float beat_freqs[2] = { f-g, f+g }; /* . . . */ } |
10.复合文字量
1 2 | struct foo { int a; char b[2];} structure; structure = (( struct foo) {x + y, ’a’, 0}); |
以上代码等价于:
1 2 3 4 | { struct foo temp = {x + y, ’a’, 0}; structure = temp; } |
再例如:
1 | char **foo = ( char *[]) { "x" , "y" , "z" }; |
1 2 3 | static struct foo x = ( struct foo) {1, ’a’, ’b’}; static int y[] = ( int []) {1, 2, 3}; static int z[] = ( int [3]) {1}; |
上面代码等价于:
1 2 3 | static struct foo x = {1, ’a’, ’b’}; static int y[] = {1, 2, 3}; static int z[] = {1, 0, 0}; |
11.指定初始化
该特性是经常使用的特性:
1 | int a[6] = { [4] = 29, [2] = 15 }; |
等价于:
1 | int a[6] = { 0, 0, 15, 0, 29, 0 }; |
指定范围及结构体的成员指定:
1 | int widths[] = { [0 ... 9] = 1, [10 ... 99] = 2, [100] = 3 }; |
1 2 3 4 5 6 7 8 | struct point { int x, y; }; struct point p = { .y = yvalue, .x = xvalue }; struct point p = { y: yvalue, x: xvalue }; union foo { int i; double d; }; union foo f = { .d = 4 }; struct point ptarray[10] = { [2].y = yv2, [2].x = xv2, [0].x = xv0 }; |
未完待续......