`
omygege
  • 浏览: 1353462 次
文章分类
社区版块
存档分类
最新评论

GCC扩展

 
阅读更多

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 把包含在括号中的复合语句看做是一个表达式,称为语句表达式,它可以出现在任何允许表达式的地方,你可以在语句表达式中使用循环、局部变量等,原本只能在复合语句中使用。例如:
  复合语句的最后一个语句应该是一个表达式,它的值将成为这个语句表达式的值。这里定义了一个安全的求最小值的宏,在标准 C 中,通常定义为:
  #define min(x,y) ((x) < (y) ? (x) : (y))
  这个定义计算 x 和 y 分别两次,当参数有副作用时,将产生不正确的结果,使用语句表达式只计算参数一次,避免了可能的错误。语句表达式通常用于宏定义。

  Typeof
  使用前一节定义的宏需要知道参数的类型,利用 typeof 可以定义更通用的宏,不必事先知道参数的类型,例如:
  这里 typeof(x) 表示 x 的值类型,第 142 行定义了一个与 x 类型相同的局部变量 _x 并初使化为 x,注意第 144 行的作用是检查参数 x 和 y 的类型是否相同。typeof 可以用在任何类型可以使用的地方,通常用于宏定义。

  零长度数组
  GNU C 允许使用零长度数组,在定义变长对象的头结构时,这个特性非常有用。例如:
  结构的最后一个元素定义为零长度数组,它不占结构的空间。在标准 C 中则需要定义数组长度为 1,分配时计算对象大小比较复杂。


  可变参数宏

  在 GNU C 中,宏可以接受可变数目的参数,就象函数一样,例如:
  这里 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] =' 的形式,例如:
  将数组的所有元素初使化为 ~0UL,这可以看做是一种简写形式。要指定结构元素,在元素值前写 'FIELDNAME:',例如:   将结构 ext2_file_operations 的元素 llseek 初始化为 generic_file_llseek,元素 read 初始化genenric_file_read,依次类推。我觉得这是 GNU C 扩展中最好的特性之一,当结构的定义变化以至元素的偏移改变时,这种初始化方法仍然保证已知元素的正确性。对于未出现在初始化中的元素,其初值为 0。

  Case 范围
  GNU C 允许在一个 case 标号中指定一个连续范围的值,例如:
  相当于
 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 用于函数,表示该函数从不返回。这可以让编译器生成稍微优化的代码,最重要的是可以消除不必要的警告信息比如未初使化的变量。例如:
  * format (ARCHETYPE, STRING-INDEX, FIRST-TO-CHECK)
  属性 format 用于函数,表示该函数使用 printf, scanf 或 strftime 风格的参数,使用这类函数最容易犯的错误是格式串与参数不匹配,指定 format 属性可以让编译器根据格式串检查参数类型。例如:
  ++++ include/linux/kernel.h?
  89: asmlinkage int printk(const char * fmt, ...)
  90: __attribute__ ((format (printf, 1, 2)));
  表示第一个参数是格式串,从第二个参数起根据格式串检查参数。
  * unused
  属性 unused 用于函数和变量,表示该函数或变量可能不使用,这个属性可以避免编译器产生警告信息。
  * section ("section-name")
  属性 section 用于函数和变量,通常编译器将函数放在 .text 节,变量放在.data 或 .bss 节,使用 section 属性,可以让编译器将函数或变量放在指定的节中。例如:
  ++++ include/linux/init.h
  78: #define __init __attribute__ ((__section__ (".text.init")))
  79: #define __exit __attribute__ ((unused, __section__(".text.exit")))
  80: #define __initdata __attribute__ ((__section__ (".data.init")))
  81: #define __exitdata __attribute__ ((unused, __section__ (".data.exit")))
  82: #define __initsetup __attribute__ ((unused,__section__ (".setup.init")))
  83: #define __init_call __attribute__ ((unused,__section__ (".initcall.init")))
  84: #define __exit_call __attribute__ ((unused,__section__ (".exitcall.exit")))
  连接器可以把相同节的代码或数据安排在一起,Linux 内核很喜欢使用这种技术,例如系统的初始化代码被安排在单独的一个节,在初始化结束后就可以释放这部分内存。
  * aligned (ALIGNMENT)
  属性 aligned 用于变量、结构或联合类型,指定变量、结构域、结构或联合的对齐量,以字节为单位,例如:
  ++++ include/asm-i386/processor.h
  294: struct i387_fxsave_struct {
  295: unsigned shortcwd;
  296: unsigned shortswd;
  297: unsigned shorttwd;
  298: unsigned shortfop;
  299: long fip;
  300: long fcs;
  301: long foo;
  ......
  308: } __attribute__ ((aligned (16)));
  表示该结构类型的变量以 16 字节对齐。通常编译器会选择合适的对齐量,显示指定对齐通常是由于体系限制、优化等原因。
  * packed
  属性 packed 用于变量和类型,用于变量或结构域时表示使用最小可能的对齐,用于枚举、结构或联合类型时表示该类型使用最小的内存。例如:
  ++++ include/asm-i386/desc.h
  51: struct Xgt_desc_struct {
  52: unsigned short size;
  53: unsigned long address __attribute__((packed));
  54: };
  域 address 将紧接着 size 分配。属性 packed 的用途大多是定义硬件相关的结构,使元素之间没有因对齐而造成的空洞。


  当前函数名

  GNU CC 预定义了两个标志符保存当前函数的名字,__FUNCTION__ 保存函数在源码中的名字__PRETTY_FUNCTION__ 保存带语言特色的名字。在 C 函数中,这两个名字是相同的,在 C++ 函数中,__PRETTY_FUNCTION__ 包括函数返回类型等额外信息,Linux 内核只使用了 __FUNCTION__。
  ++++ fs/ext2/super.c
  98: void ext2_update_dynamic_rev(struct super_block *sb)
  99: {
  100: struct ext2_super_block *es = EXT2_SB(sb)->s_es;
  101:
  102: if (le32_to_cpu(es->s_rev_level) > EXT2_GOOD_OLD_REV)
  103: return;
  104:
  105: ext2_warning(sb, __FUNCTION__,
  106: "updating to rev %d because of new feature flag, "
  107: "running e2fsck is recommended",
  108: 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 表示当前函数的调用者的返回地址,依此类推。例如:
  ++++ kernel/sched.c
  437: printk(KERN_ERR "schedule_timeout: wrong timeout "
  438: "value %lx from %p/n", timeout,
  439: __builtin_return_address(0));
  * __builtin_constant_p(EXP)
  内建函数 __builtin_constant_p 用于判断一个值是否为编译时常数,如果参数EXP 的值是常数,函数返回 1,否则返回 0。例如:
  ++++ include/asm-i386/bitops.h
  249: #define test_bit(nr,addr) /
  250: (__builtin_constant_p(nr) ? /
  251:constant_test_bit((nr),(addr)) : /
  252:variable_test_bit((nr),(addr)))
  很多计算或操作在参数为常数时有更优化的实现,在 GNU C 中用上面的方法可以根据参数是否为常数,只编译常数版本或非常数版本,这样既不失通用性,又能在参数是常数时编译出最优化的代码。
  * __builtin_expect(EXP, C)
  内建函数 __builtin_expect 用于为编译器提供分支预测信息,其返回值是整数表达式 EXP 的值,C 的值必须是编译时常数。例如:
  ++++ include/linux/compiler.h
  13: #define likely(x) __builtin_expect((x),1)
  14: #define unlikely(x) __builtin_expect((x),0)
  ++++ kernel/sched.c
  564: if (unlikely(in_interrupt())) {
  565: printk("Scheduling in interrupt/n");
  566: BUG();
  567: }
  这个内建函数的语义是 EXP 的预期值是 C,编译器可以根据这个信息适当地重排语句块的顺序,使程序在预期的情况下有更高的执行效率。上面的例子表示处于中断上下文是很少发生的,第 565-566 行的目标码可能会放在较远的位置,以保证经常执行的目标码更紧凑。


(typeof独说)

FROM http://gcc.gnu.org/onlinedocs/gcc/Typeof.html#Typeof )
Another way to refer to the type of an expression is with typeof. The syntax of using of this keyword looks like sizeof, but the construct acts semantically like a type name defined with typedef.
There are two ways of writing the argument to typeof: with an expression or with a type. Here is an example with an expression:
typeof (x[0](1))
This assumes that x is an array of pointers to functions; the type described is that of the values of the functions.
Here is an example with a typename as the argument:
typeof (int *)
Here the type described is that of pointers to int.
If you are writing a header file that must work when included in ISO C programs, write __typeof__ instead of typeof. See Alternate Keywords.
A typeof-construct can be used anywhere a typedef name could be used. For example, you can use it in a declaration, in a cast, or inside of sizeof or typeof.
typeof is often useful in conjunction with the statements-within-expressions feature. Here is how the two together can be used to define a safe “maximum” macro that operates on any arithmetic type and evaluates each of its arguments exactly once:
#define max(a,b) /
({ typeof (a) _a = (a); /
typeof (b) _b = (b); /
_a > _b ? _a : _b; })
The reason for using names that start with underscores for the local variables is to avoid conflicts with variable names that occur within the expressions that are substituted for a and b. Eventually we hope to design a new form of declaration syntax that allows you to declare variables whose scopes start only after their initializers; this will be a more reliable way to prevent such conflicts.
Some more examples of the use of typeof:
This declares y with the type of what x points to.
typeof (*x) y;
This declares y as an array of such values.
typeof (*x) y[4];
This declares y as an array of pointers to characters:
typeof (typeof (char *)[4]) y;
It is equivalent to the following traditional C declaration:
char *y[4];
To see the meaning of the declaration using typeof, and why it might be a useful way to write, rewrite it with these macros:
#define pointer(T) typeof(T *)
#define array(T, N) typeof(T [N])
Now the declaration can be rewritten this way:
array (pointer (char), 4) y;
Thus, array (pointer (char), 4) is the type of arrays of 4 pointers to char.
Compatibility Note: In addition to typeof, GCC 2 supported a more limited extension which permitted one to write
typedef T = expr;
with the effect of declaring T to have the type of the expression expr. This extension does not work with GCC 3 (versions between 3.0 and 3.2 will crash; 3.2.1 and later give an error). Code which relies on it should be rewritten to use typeof:
typedef typeof(expr) T;
This will work with all versions of GCC。
分享到:
评论

相关推荐

    gcc扩展用法gcc扩展用法

    gcc扩展用法gcc扩展用法gcc扩展用法

    Gcc扩展之typeof

    本文描述了,gcc编译器扩展的typeof运算的用法。

    GCC核心扩展.doc

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

    Keil5配置GCC编译器编译STM32工程示例

    Keil5配置GCC编译器编译STM32工程,具体配置可以参考我的博客http://my.csdn.net/weixin_39871788

    GCC的__attribute__扩展功能

    在学习linux内核代码及一些开源软件的源码,经常可以看到有关__attribute__的相关使用。本文结合自己的学习经历,较为详细的介绍了__attribute__相关语法及其使用。

    Linux gcc.pdf

    Linux系统C/C++ GCC编译器学习gcc.pdf详解GCC编译原理,有助于理解linxu编译原理

    gcc使用教程 讲解gcc

    Linux 中最重要的软件开发工具是 GCC。GCC 是 GNU 的 C 和 C++ 编译器。实际上,GCC 能够编译三种语言:C、C++ 和 Object C(C 语言的一种面向对象扩展)。利用 gcc 命令可同时编译并连接 C 和 C++ 源程序。

    linux下载安装gcc编译器

    GCC是以GPL许可证所发行的自由软件,也是GNU计划的关键部分。GCC的初衷是为GNU操作系统...但其很快扩展,变得可处理C++,后来又扩展为能够支持更多编程语言,如Fortran、Pascal、Objective -C、Java、Ada、Go以及各类处

    GCC的51项扩充(编译器扩展语法标准)

    对于程序员来说,标准对于咱们是很重要的,目前gcc对标准C进行了51相扩充,希望你早点用上

    GCC官方文档(英文版)

    但其很快扩展,变得可处理C++,后来又扩展为能够支持更多编程语言,如Fortran、Pascal、Objective -C、Java、Ada、Go以及各类处理器架构上的汇编语言等,所以改名GNU编译器套件(GNU Compiler Collection)

    gcc最新版下载

    GCC(GNU Compiler Collection,GNU编译器套装),是一套由GNU开发的编程语言编译器。...GCC很快地扩展,变得可处理C++。之后也变得可处理Fortran、Pascal、Objective-C、Java、Ada,以及Go与其他语言。

    libstdc++.so.6.0.21

    libstdc++.so.6.0.21 gcc扩展. 出现报错/lib64/libstdc++.so.6: version `GLIBCXX_3.4.21‘ not found时可使用此扩展替换原来的低版本扩展

    gcc使用手册

    GCC(GNU Compiler Collection,GNU编译器集合)是一套由GNU工程开发的支持多种编程语言的...GCC很快地扩展,并支持处理C++。后来又扩展能够支持更多编程语言,如Fortran、Pascal、Objective-C、Java、Ada、Go等。

    Linux Unix GCC C/C++ 语言编译器 中文手册

    GCC(GNU Compiler Collection,GNU编译器套装)是一套由GNU工程开发的支持多种编程语言的...GCC很快地扩展,并支持处理C++。后来又扩展能够支持更多编程语言,如Fortran、Pascal、Objective-C、Java、Ada、Go等。

    嵌入式gcc跨平台编译器

    它是一套以 GPL 及 LGPL 许可证所发行的自由软件,也是 GNU计划 的关键部分,亦是自由的 类Unix 及...GCC 很快地扩展,变得可处理 C++。之后也变得可处理 Fortran、Pascal、Objective-C、Java,以及 Ada 与其他语言。

    LINUX内核中的GCC特性

    GCC支持的C语言扩展特性,LINUX内核的GCC特性

    gcc与gdb使用

    概述gcc,gdb。GCC 原名为 GNU C 语言编译器,因为它原本只能处理 C语言。GCC 很快地扩展,变得可处理 C++。之后也变得可处理 Fortran、Pascal、Objective-C、Java, 以及 Ada与其他语言

    linux gcc 编程必须

    GCC(GNU Compiler Collection,GNU编译器套装),是一套由 GNU 开发的编程语言编译器。...GCC 很快地扩展,变得可处理 C++。之后也变得可处理 Fortran、Pascal、Objective-C、Java, 以及 Ada与其他语言。[1]

    GCC,the Complete Reference

    一个事实是所有的自由...伴随着最近加入GCC家族的两种语言--Java和Ada--GCC编译程序的羽翼扩展得更大更广了。这使得GCC可用的语言达到6种:C、C++、Objective-C、Fortran、Java和Ada。而且它还在不断地开发其他语言,

    gcc使用大全

    GCC(GNU Compiler Collection,GNU编译器套装),是一套由 GNU 开发的编程语言编译器。...GCC 很快地扩展,变得可处理 C++。之后也变得可处理 Fortran、Pascal、Objective-C、Java, 以及 Ada与其他语言。

Global site tag (gtag.js) - Google Analytics