前言
Linux内核是基于C语言编写的,熟练掌握C语言是深入学习Linux内核的基本要求。
GNU C语言的扩展
GCC的C编译器除了支持ANSI C标准之外,还对C语言进行了很多的扩充。
这些扩充对代码优化、目标代码布局以及安全检查等方面提供了很强的支持,因此支持GNU扩展的C语言称为GNU C语言。
Linux内核采用GCC编译器,所以Linux内核的代码自然使用了很多GCC的新扩充特性。
本章介绍一些GCC C语言扩充的新特性,希望读者在学习Linux内核时特别留意。
(1)语句表达式
在GNU C语言中,括号里的复合语句可以看作一个表达式,称为语句表达式。
在一个语句表达式里,可以使用循环、跳转和局部变量等。这个特性通常用在宏定义中,可以让宏定义变得更安全,如比较两个值的大小。
#define max(a,b) ((a) > (b) ? (a) : (b))
上述代码会导致安全问题,a和b有可能会计算两次,比如a传入i++,b传入j++。在GNU C语言中,如果知道a和b的类型,可以这样写这个宏。
#define maxint(a,b) \ ({int _a = (a), _b = (b); _a > _b ? _a : _b; })
如果你不知道a和b的类型,还可以使用typeof类转换宏。
<include/linux/kernel.h>#define min(x, y) ({ \ typeof(x) _min1 = (x); \ typeof(y) _min2 = (y); \ (void) (&_min1 == &_min2); \ _min1 < _min2 ? _min1 : _min2; })
typeof也是GNU C语言的一个扩充用法,可以用来构造新的类型,通常和语句表达式一起使用。
下面是一些例子。
typeof (*x) y; typeof (*x) z[4]; typeof (typeof (char *)[4]) m;
- 第一句声明y是x指针指向的类型。
- 第二句声明z是一个数组,其中数组的类型是x指针指向的类型。
- 第三句声明m是一个指针数组,和char*m[4]声明是一样的。
(2)零长数组
GNU C语言允许使用变长数组,这在定义数据结构时非常有用。
<mm/percpu.c> struct pcpu_chunk { struct list_head list; unsigned long populated[]; /* 变长数组 */};
数据结构最后一个元素被定义为零长度数组,不占结构体空间。
这样,我们可以根据对象大小动态地分配结构的大小。
struct line { int length; char contents[0]; };
struct line *thisline = malloc(sizeof(struct line) +this_length); thisline->length = this_length;
如上例所示,struct line数据结构定义了一个int length变量和一个变长数组contents[0],这个struct line数据结构的大小只包含int类型的大小,不包含contents的大小,也就是sizeof (struct line) =sizeof (int)。
创建结构体对象时,可根据实际的需要指定这个可变长数组的长度,并分配相应的空间,如上述实例代码分配了this_length 字节的内存,并且可以通过contents[index]来访问第index个地址的数据。
(3)case范围
GNU C语言支持指定一个case的范围作为一个标签,如:
case low ...high: