免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1104 | 回复: 1
打印 上一主题 下一主题

GNU C扩展 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-11-29 20:54 |只看该作者 |倒序浏览
GNU C是一个功能非常强大的跨平台C编译器,它对C语言提供了很多扩展,这些扩展对优化、目标代码布局、更安全的检查等方面提供了很强的支持。本文把支持GNU扩展的C语言称为GNU C。
   
Linux内核代码使用了大量的GNU C扩展,以至于能够编译Linux内核的唯一编译器是GNU CC,以前甚至出现过编译
Linux内核要使用特殊的GNU CC版本的情况。本文是对Linux内核使用的GNU
C扩展的一个汇总,希望当你读内核源码遇到不理解的语法和语义时,能从本文找到一个初步的解答,更详细的信息可以查看gcc.info。
[color="#0000ff"]语句表达式
------------------------------------------
GNU C把包含在括号中的复合语句看做是一个表达式,称为语句表达式,它可以出现在任何允许表达式的地方,你可以在语句表达式中使用循环、局部变量等,原本只能在复合语句中使用。例如:
    #define min_t(type, x, y) ({ type __x = x;\
                                 type __y = y;\
                                 [color="#800000"]__x
[color="#0000ff"]零长度数组
------------------------------------------
    GNU C允许使用零长度数组,在定义变长对象的头结构时,这个特性非常有用。例如:
    struct minix_dir_entry {
        __u16 inode;
        char name[0];
    };
结构的最后一个元素定义为零长度数组,它不占结构的空间。在标准C中则需要定义数组长度为1,分配时计算对象大小比较复杂。
可变参数宏
------------------------------------------
    在 GNU C中,宏可以接受可变数目的参数,就象函数一样,例如:
    #define KERN_DEBUG ""
    #define pr_debug(fmt,arg...) printk(KERN_DEBUG 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")
注意最后没有逗号。
标号元素
------------------------------------------

准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。
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':
声明的特殊属性
------------------------------------------
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(ARCHETYPE, STRING-INDEX, FIRST-TO-CHECK)
属性format用于函数,表示该函数使用printf, scanf 或strftime风格的参数,使用这类函数最容易犯的错误是格式串与参数不匹配,指定format属性可以让编译器根据格式串检查参数类型。例如:
    asmlinkage int printk(const char * fmt, ...)
    __attribute__ ((format (printf, 1, 2)));
表示第一个参数是格式串,从第二个参数起根据格式串检查参数。
* unused
属性unused用于函数和变量,表示该函数或变量可能不使用,这个属性可以避免编译器产生警告信息。
* section ("section-name")
属性section用于函数和变量,通常编译器将函数放在.text节,变量放在.data或 .bss 节,使用section属性,可以让编译器将函数或变量放在指定的节中。例如:
    #define __init __attribute__ ((__section__ (".text.init")))
    #define __exit __attribute__ ((unused, __section__(".text.exit")))
    #define __initdata __attribute__ ((__section__ (".data.init")))
    #define __exitdata __attribute__ ((unused, __section__ (".data.exit")))
    #define __initsetup __attribute__ ((unused,__section__ (".setup.init")))
    #define __init_call __attribute__ ((unused,__section__ (".initcall.init")))
    #define __exit_call __attribute__ ((unused,__section__ (".exitcall.exit")))
    连接器可以把相同节的代码或数据安排在一起,Linux内核很喜欢使用这种技术,例如系统的初始化代码被安排在单独的一个节,在初始化结束后就可以释放这部分内存。
* aligned (ALIGNMENT)
属性aligned用于变量、结构或联合类型,指定变量、结构域、结构或联合的对齐量,以字节为单位,例如:
        struct i387_fxsave_struct {
            unsigned short cwd;
            unsigned short swd;
            unsigned short twd;
            unsigned short fop;
        } __attribute__ ((aligned (16)));
    表示该结构类型的变量以16字节对齐。通常编译器会选择合适的对齐量,显示指定对齐通常是由于体系限制、优化等原因。
* packed
属性packed用于变量和类型,用于变量或结构域时表示使用最小可能的对齐,用于枚举、结构或联合类型时表示该类型使用最小的内存。例如:
        struct Xgt_desc_struct {
            unsigned short size;
            unsigned long address __attribute__((packed));
        };
域address将紧接着size分配。属性packed的用途大多是定义硬件相关的结构,使元素之间没有因对齐而造成的空洞。
当前函数名
------------------------------------------
    GNU CC预定义了两个标志符保存当前函数的名字,__FUNCTION__保存函数在源码中的名字;__PRETTY_FUNCTION__
保存带语言特色的名字。在C函数中,这两个名字是相同的,在C++函数中,__PRETTY_FUNCTION__包括函数返回类型等额外信息,Linux内核只使用了__FUNCTION__。
        void ext2_update_dynamic_rev(struct super_block *sb) {
            struct ext2_super_block *es = EXT2_SB(sb)->s_es;
            if (le32_to_cpu(es->s_rev_level) > EXT2_GOOD_OLD_REV)
            return;
            ext2_warning(sb, __FUNCTION__,
            "updating to rev %d because of new feature flag, "
            "running e2fsck is recommended",
            EXT2_DYNAMIC_REV);
   
这里__FUNCTION__将被替换为字符串 "ext2_update_dynamic_rev"。虽然__FUNCTION__看起来类似于标准
C中的__FILE__,但实际上__FUNCTION__是被编译器替换的,不象__FILE__ 被预处理器替换。
内建函数
------------------------------------------
GNU C提供了大量的内建函数,其中很多是标准C库函数的内建版本,例如memcpy,它们与对应的C库函数功能相同,本文不讨论这类函数,其他内建函数的名字通常以__builtin开始。
* __builtin_return_address (LEVEL)
内建函数__builtin_return_address返回当前函数或其调用者的返回地址,参数LEVEL指定在栈上搜索框架的个数,0表示当前函数的返回地址,1表示当前函数的调用者的返回地址,依此类推。例如:
        printk(    KERN_ERR "schedule_timeout: wrong timeout "
                "value %lx from %p\n", timeout,
                __builtin_return_address(0));
* __builtin_constant_p(EXP)
内建函数__builtin_constant_p用于判断一个值是否为编译时常数,如果参数EXP的值是常数,函数返回 1,否则返回 0。例如:
        #define test_bit(nr,addr) \
            (__builtin_constant_p(nr) ? \
            constant_test_bit((nr),(addr)) : \
            variable_test_bit((nr),(addr)))
    很多计算或操作在参数为常数时有更优化的实现,在GNU C中用上面的方法可以根据参数是否为常数,只编译常数版本或非常数版本,这样既不失通用性,又能在参数是常数时编译出最优化的代码。
* __builtin_expect(EXP, C)
    内建函数__builtin_expect用于为编译器提供分支预测信息,其返回值是整数表达式EXP的值,C的值必须是编译时常数。例如:
      13: #define likely(x) __builtin_expect((x),1)
      14: #define unlikely(x) __builtin_expect((x),0)
      564: if (unlikely(in_interrupt())) {
      565: printk("Scheduling in interrupt\n");
      566: BUG();
      567: }
   
这个内建函数的语义是 EXP 的预期值是
C,编译器可以根据这个信息适当地重排语句块的顺序,使程序在预期的情况下有更高的执行效率。上面的例子表示处于中断上下文是很少发生的,第
565-566 行的目标码可能会放在较远的位置,以保证经常执行的目标码更紧凑。
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/67414/showart_1672137.html

论坛徽章:
0
2 [报告]
发表于 2008-11-30 10:39 |只看该作者
谢谢,我的收藏了
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP