Chinaunix首页 | 论坛 | 博客
  • 博客访问: 276704
  • 博文数量: 28
  • 博客积分: 690
  • 博客等级: 上士
  • 技术积分: 358
  • 用 户 组: 普通用户
  • 注册时间: 2011-08-25 20:39
文章分类

全部博文(28)

文章存档

2012年(12)

2011年(16)

分类: C/C++

2011-12-14 14:58:33

1.inline函数
GCC和C99中inline函数的用法是不同的。
1.1相同点:static inline函数
用static修饰的inline函数,在GCC和C99中的意义是相同的,编译器并不单独编译inline函数内的代码,不会单独生成汇编代码段,而是在编译的时候展开到所有调用inline函数地方;因为有static的修饰,所以只有包含inline函数定义的代码才可以调用此inline函数。 
1.2不同点:不加static的inline函数
在没有static修饰的情况下,GCC和C99标准对inline函数的解释是不同的。GCC中,可以认为它是一个普通全局函数加上了inline的属性。即在其定义所在文件内,它的表现和static inline一致:在能展开的时候会被内联展开编译。但是为了能够在文件外调用它,gcc一定会为它生成一份独立的汇编码,以便在外部进行调用。即从文件外部看来,它和一个普通的extern的函数无异。C99中,C99的inline的使用相当令人费解。当一个定义为inline的函数没有被声明为extern的时候,其表现有点类似于gcc中extern inline那样。即如果一个inline函数在文件范围内没有被声明为extern的话,这个函数在文件内的表现就和gcc的extern inline相似:在本文件内调用时允许编译器使用本文件内定义的这个内联版本,但同时也允许外部存在同名的全局函数。只是比较奇怪的是C99居然没有指定编译器是否必须在本文件内使用这个inline的版本而是让编译器厂家自己来决定,相当模糊的定义。

2.const 用法
          
2.1     Const 用来修饰输入参数时,可以用来防止输入参数被修改;所以当修饰用来返回数据的函数参数时,不能用Const修饰 。可以用Const修饰引用类型的结构体参数,这样可以提高效率的同时,保证参数不被修改。

           2.2    Const用来修饰返回值时,可以保证返回值不被修改,此时只对返回数据的引用时,有用。

           2.3    Const用来修饰成员函数时,可以保证Const函数不修改类的数据成员。

GNU  C 须知
1.零长度数组
GNU C 允许使用零长度数组,在定义变长对象的头结构时,这个特性非常有用。例如:
     struct minix_dir_entry {
     __u16 inode;
     char name[0];
};
结构的最后一个元素定义为零长度数组,它不占结构的空间。在标准 C 中则需要定义数组长度为 1,分配时计算对象大小比较复杂。
2.case范围
GNU C 允许在一个 case 标号中指定一个连续范围的值,例如:
     case '0' ... '9': c -= '0'; break;
     case 'a' ... 'f': c -= 'a'-10; break;
     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':
3.语句表达式
GNU C 把包含在括号中的复合语句看做是一个表达式,称为语句表达式,它可以出现在任何允许表达式的地方,你可以在语句表达式中使用循环、局部变量等,原本只能在复合语句中使用。例如:
     #define min_t(type,x,y) \
     ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
复合语句的最后一个语句应该是一个表达式,它的值将成为这个语句表达式的值。这里定义了一个安全的求最小值的宏,在标准 C 中,通常定义为:
     #define min(x,y) ((x) < (y) ? (x) : (y))
这个定义计算 x 和 y 分别两次,当参数有副作用时(比如出现参数自增或自减语句时),将产生不正确的结果,使用语句表达式只计算参数一次,避免了可能的错误。语句表达式通常用于宏定义。
4.typeof关键字
使用前一节定义的宏需要知道参数的类型,利用 typeof 可以定义更通用的宏,不
必事先知道参数的类型,例如:
#define min(x,y) ({ \
   const typeof(x) _x = (x);    \
   const typeof(y) _y = (y);    \
   (void) (&_x == &_y);          \
   _x < _y ? _x : _y; })
这里 typeof(x) 表示 x 的值类型, const typeof(x) _x = (x); 中定义了一个与 x 类型相同的局部变量 _x 并初使化为 x, (void) (&_x == &_y); 的作用是检查参数 x 和 y 的类型是否相同。typeof 可以用在任何类型可以使用的地方,通常用于宏定义。
5.可变参数的宏
在 GNU C 中,宏可以接受可变数目的参数,就象函数一样,例如:
     #define pr_debug(fmt,arg...) \
     printk(fmt,##arg)
这里 arg 表示其余的参数,可以是零个或多个,这些参数以及参数之间的逗号构成 arg 的值,在宏扩展时替换 arg,例如:
     pr_debug("%s:%d",filename,line)
会被扩展为
     printk("%s:%d", filename, line)
使用 ## 的原因是处理 arg 不匹配任何参数的情况,这时 arg 的值为空,GNU C 预处理器在这种特殊情况下,丢弃 ## 之前的逗号,这样
     pr_debug("success!\n")
会被扩展为
     printk("success!\n")
而不是printk("success!\n",)注意最后的逗号。
6.标号元素
标准 C 要求数组或结构变量的初使化值必须以固定的顺序出现,在 GNU C 中,通过指定索引或结构域名,允许初始化值以任意顺序出现。指定数组索引的方法是在初始化值前写 '[INDEX] =',要指定一个范围使用 '[FIRST ... LAST] =' 的形式,例如:
static unsigned long irq_affinity [NR_IRQS] = { [0 ... NR_IRQS-1] = ~0UL };
将数组的所有元素初使化为 ~0UL,这可以看做是一种简写形式。
要指定结构元素,在元素值前写 'FIELDNAME:',例如:
struct file_operations ext2_file_operations = {
      llseek:       generic_file_llseek,
      read:           generic_file_read,
      write:       generic_file_write,
      ioctl:       ext2_ioctl,
      mmap:           generic_file_mmap,
      open:           generic_file_open,
      release:        ext2_release_file,
      fsync:       ext2_sync_file,
};
将结构 ext2_file_operations 的元素 llseek 初始化为 generic_file_llseek,元素 read 初始化为 genenric_file_read,依次类推。我觉得这是 GNU C 扩展中最好的特性之一,当结构的定义变化以至元素的偏移改变时,这种初始化方法仍然保证已知元素的正确性。对于未出现在初始化中的元素,其初值为 0。
7.当前函数名
GNU CC 预定义了两个标志符保存当前函数的名字,__FUNCTION__ 保存函数在源码中的名字,__PRETTY_FUNCTION__ 保存带语言特色的名字。在 C 函数中,这两个名字是相同的,在 C++ 函数中,__PRETTY_FUNCTION__ 包括函数返回类型等额外信息,Linux 内核只使用了 __FUNCTION__。
void example()
{
     printf{“This is function:%s”, __FUNCTION__};
}
代码中__FUNCTION__意味着字符串“example”。
8.特殊属性声明
GNU C 允许声明函数、变量和类型的特殊属性,以便手工的代码优化和更仔细的代码检查。要指定一个声明的属性,在声明后写   __attribute__ (( ATTRIBUTE ))其中 ATTRIBUTE 是属性说明,多个属性以逗号分隔。GNU C 支持十几个属性,这里介绍最常用的:
* noreturn
属性 noreturn 用于函数,表示该函数从不返回。这可以让编译器生成稍微优化的代码,最重要的是可以消除不必要的警告信息比如未初使化的变量。例如:
# define ATTRIB_NORET   __attribute__((noreturn))....
asmlinkage NORET_TYPE void do_exit(long error_code) ATTRIB_NORET;
* format
属性 format 用于函数,表示该函数使用 printf, scanf 或 strftime 风格的参数,使用这类函数最容易犯的错误是格式串与参数不匹配,指定 format 属性可以让编译器根据格式串检查参数类型。例如:
asmlinkage int printk(const char * fmt, ...)    __attribute__ ((format (printf, 1, 2)));
表示第一个参数是格式串,从第二个参数起根据格式串检查参数。
* unused
属性 unused 用于函数和变量,表示该函数或变量可能不使用,这个属性可以避免编译器产生警告信息。
* aligned
属性 aligned 用于变量、结构或联合类型,指定变量、结构域、结构或联合的对齐量,以字节为单位,例如:
struct example_struct
{
     char a;
     int   b;
     long c;
} __attribute__((aligned(4)));
表示该结构类型的变量以4字节对界。
* packed
属性 packed 用于变量和类型,用于变量或结构域时表示使用最小可能的对齐,用于枚举、结构或联合类型时表示该类型使用最小的内存。例如:
struct example_struct
{
     char a;
     int b__attribute__ ((packed));
     long c__attribute__((packed));
};
对于结构体example_struct而言,在i386平台下,其sizeof的结果为9,如果删除其中的2个—   attribute__((packed)),其sizeof将为12.
9.内建函数
GNU C 提供了大量的内建函数,其中很多是标准 C 库函数的内建版本,例如memcpy,它们与对应的 C 库函数功能相同,不属于库函数的其他内建函数的名字通常以 __builtin 开始。例如:
* __builtin_return_address (LEVEL)
     内建函数 __builtin_return_address 返回当前函数或其调用者的返回地址,参数 LEVEL 指定在栈上搜索框架的个数,0 表示当前函数的返回地址,1 表示当前函数的调用者的返回地址,依此类推。
* __builtin_constant_p(EXP)
     内建函数 __builtin_constant_p 用于判断一个值是否为编译时常数,如果参数EXP 的值是常数,函数返回 1,否则返回 0。
* __builtin_expect(EXP, C)
     内建函数 __builtin_expect 用于为编译器提供分支预测信息,其返回值是整数表
达式 EXP 的值,C 的值必须是编译时常数。
例如,下面的代码检测第一个参数是否为编译时常数以确定采用参数版本还是非参数版本代码:
#define test_bit(nr,addr) \
    (__builtin_constant_p(nr) ? \
    constant_test_bit((nr),(addr)) : \
    variable_test_bit((nr),(addr)))

阅读(2335) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~