Chinaunix首页 | 论坛 | 博客
  • 博客访问: 583000
  • 博文数量: 199
  • 博客积分: 5087
  • 博客等级: 大校
  • 技术积分: 2165
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-26 21:53
文章存档

2010年(199)

我的朋友

分类: C/C++

2010-08-17 11:59:28

GNU C 扩展
GNC CC是一个功能非常强大的跨平台C编译器,它对C 语言提供了很多扩展,这些扩展对优化、目标代码布局、更安全的检查等方面提供了很强的支持。本文把支持GNU 扩展的C 语言称为GNU C。 

   Linux 内核代码使用了大量的 GNU C 扩展,以至于能够编译 Linux 内核的唯一编译器是 GNU CC,以前甚至出现过编译 Linux 内核要使用特殊的 GNU CC 版本的情况。本文是对 Linux 内核使用的 GNU C 扩展的一个汇总,希望当你读内核源码遇到不理解的语法和语义时,能从本文找到一个初步的解答,更详细的信息可以查看gcc.info。文中的例子取自 Linux 2.4.18。 

  语句表达式 

  GNU C 把包含在括号中的复合语句看做是一个表达式,称为语句表达式,它可以出现在任何允许表达式的地方,你可以在语句表达式中使用循环、局部变量等,原本只能在复合语句中使用。例如: 

++++ include/linux/kernel.h 
159: #define min_t(type,x,y) \ 
160: ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) 
++++ net/ipv4/tcp_output.c 
654: int full_space = min_t(int, tp->window_clamp, tcp_full_space(sk)); 

  复合语句的最后一个语句应该是一个表达式,它的值将成为这个语句表达式的值。这里定义了一个安全的求最小值的宏,在标准 C 中,通常定义为: 

#define min(x,y) ((x) < (y) ? (x) : (y)) 

  这个定义计算 x 和 y 分别两次,当参数有副作用时,将产生不正确的结果,使用语句表达式只计算参数一次,避免了可能的错误。语句表达式通常用于宏定义。 

  Typeof 

  使用前一节定义的宏需要知道参数的类型,利用 typeof 可以定义更通用的宏,不必事先知道参数的类型,例如: 

++++ include/linux/kernel.h 
141: #define min(x,y) ({ \ 
142: const typeof(x) _x = (x); \ 
143: const typeof(y) _y = (y); \ 
144: (void) (&_x == &_y); \ 
145: _x < _y ? _x : _y; }) 

  这里 typeof(x) 表示 x 的值类型,第 142 行定义了一个与 x 类型相同的局部变量 _x 并初使化为 x,注意第 144 行的作用是检查参数 x 和 y 的类型是否相同。typeof 可以用在任何类型可以使用的地方,通常用于宏定义。 

  零长度数组 

  GNU C 允许使用零长度数组,在定义变长对象的头结构时,这个特性非常有用。例如: 

++++ include/linux/minix_fs.h 
85: struct minix_dir_entry { 
86: __u16 inode; 
87: char name[0]; 
88: }; 

  结构的最后一个元素定义为零长度数组,它不占结构的空间。在标准 C 中则需要定义数组长度为 1,分配时计算对象大小比较复杂。 

  可变参数宏 

  在 GNU C 中,宏可以接受可变数目的参数,就象函数一样,例如: 

++++ include/linux/kernel.h 
110: #define pr_debug(fmt,arg...) \ 
111: printk(KERN_DEBUG fmt,##arg) 

  这里 arg 表示其余的参数,可以是零个或多个,这些参数以及参数之间的逗号构成 arg 的值,在宏扩展时替换 arg,例如: 

pr_debug("%s:%d",filename,line) 

  扩展为 

printk("<7>" "%s:%d", filename, line) 

  使用 ## 的原因是处理 arg 不匹配任何参数的情况,这时 arg 的值为空,GNUC 预处理器在这种特殊情况下,丢弃 ## 之前的逗号,这样 

pr_debug("success!\n") 

  扩展为 

printk("<7>" "success!\n") 

  注意最后没有逗号。 

  标号元素 

  标准 C 要求数组或结构变量的初使化值必须以固定的顺序出现,在 GNU C 中,通过指定索引或结构域名,允许初始化值以任意顺序出现。指定数组索引的方法是在初始化值前写 '[INDEX] =',要指定一个范围使用 '[FIRST ... LAST] =' 的形式,例如: 

+++++ arch/i386/kernel/irq.c 
1079: static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL }; 

  将数组的所有元素初使化为 ~0UL,这可以看做是一种简写形式。要指定结构元素,在元素值前写 'FIELDNAME:',例如: 

++++ fs/ext2/file.c 
41: struct file_operations ext2_file_operations = { 
42: llseek: generic_file_llseek, 
43: read: generic_file_read, 
44: write: generic_file_write, 
45: ioctl: ext2_ioctl, 
46: mmap: generic_file_mmap, 
47: open: generic_file_open, 
48: release: ext2_release_file, 
49: fsync: ext2_sync_file, 
50 }; 

  将结构 ext2_file_operations 的元素 llseek 初始化为 generic_file_llseek,元素 read 初始化genenric_file_read,依次类推。我觉得这是 GNU C 扩展中最好的特性之一,当结构的定义变化以至元素的偏移改变时,这种初始化方法仍然保证已知元素的正确性。对于未出现在初始化中的元素,其初值为 0。 

  Case 范围 

  GNU C 允许在一个 case 标号中指定一个连续范围的值,例如: 

++++ arch/i386/kernel/irq.c 
1062: case '0' ... '9': c -= '0'; break; 
1063: case 'a' ... 'f': c -= 'a'-10; break; 
1064: case 'A' ... 'F': c -= 'A'-10; break; 

case '0' ... '9': 

  相当于 

case '0': case '1': case '2': case '3': case '4': 
case '5': case '6': case '7': case '8': case '9': 


  声明的特殊属性 

  GNU C 允许声明函数、变量和类型的特殊属性,以便手工的代码优化和更仔细的代码检查。要指定一个声明的属性,在声明后写 
__attribute__ (( ATTRIBUTE )) 

  其中 ATTRIBUTE 是属性说明,多个属性以逗号分隔。GNU C 支持十几个属性,这里介绍最常用的: 

摘自:中国IT实验室
阅读(974) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~