Chinaunix首页 | 论坛 | 博客
  • 博客访问: 115659
  • 博文数量: 22
  • 博客积分: 835
  • 博客等级: 准尉
  • 技术积分: 260
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-25 21:23
文章分类

全部博文(22)

文章存档

2011年(1)

2009年(21)

我的朋友

分类: LINUX

2009-11-25 14:30:29

对于GCC的学习我觉得包括以下几个方面:
一、GCC的总体认识
 有三个错误的观念必须得到纠正:
 1)GCC官方的定义是:GNU Complier Collection,而不是我们常说的GNU C Complier。因为GCC现在除了支持C外还支持C++/Java/Fortran等语言。
 2)GCC其实只是编译系统的驱动程序,通过它来解析不同输入参数,并依次调用预处理器(cpp),编译器(cc1/cc1plus),汇编器(as),链接器(ld)生成一个可执行文件的过程。
 3)GCC和G++的区别并不是前者用来编译C代码,后者用来编译C++代码。它们的区别是GCC把.c文件当C代码处理(cc1编译),而G++则当C++处理(cc1plus编译)。对于.cpp文件gcc和g++的处理过程没有什么不同。另一个区别是GCC默认不能链接C++库(除非加上-lstdc++选项)而G++可以。g++其实是"封装"了gcc进行处理的。 
 
二、GCC的常用选项
 在网络上对于GCC的所有选项的翻译说明到处可以找到,如下地址则可以直接下载得到:
 
 其实GCC的选项都是传递给预处理器,编译器,汇编器,链接器选项用的,当然这些“器”在单独使用的时候还有自己特有的一些选项,具体可以查询个"器"的GNU手册.
 
 我根据实际应用中的经验,把最常用的选项作如下整理:
 
 1)控制GCC处理的四个过程选项:
 gcc -E  test.c -o test.i   //把原代码文件交给cpp预处理器生成一个经过预处理之后的中间文件*.i/*.ii(c++)
 GCC -S test.i -o test.s    //把经过预处理之后的*.i文件交给编译器cc1生成*.s文件
 GCC -c test.s -o test.o    //把经过编译后生成的汇编文件*.s交给as进行汇编,生成目标.o文件
 GCC test.o [-o test]    //将as汇编之后的目标文件交给ld链接成一个可执行的文件test
 要了解上面四个过程的详细情况,只需要在后面加上-v选项 
 GCC -o test -save-temps first.c second.c third.c  //将后面的三个文件编译后链接成一个可执行文件test,并且保存所有的中间临时文件
 
 2)其他常用选项
 -Wa  //指示后面参数是传递给as汇编器的
 -Wl  //指示后面的参数是传递给ld链接器的
 -I   //提示编译器寻找头文件的目录或路径
 -L  //搜索除系统标准库目录外的其他目录
 -Bprefix//指定编译器驱动程序需要执行的其他文件的目录前缀,比如cpp/cc1目录前缀
 -lname //链接名为libname.a/so地库
 
 -w  //禁止所有告警信息
 -Wall  //提示所有的常见警告,另外的一些不常见的告警不在这个范围内
 -Werror //出现警告则停止向下编译
 
 -MD/MMD //生成同名的依赖文件,后者忽略了系统头文件的修改
 -ggdb //以本地格式输出调试信息,尽可能包括GDB扩展,一般要产生调试信息都用这个选项,其他项不考虑
 -0X  //编译器启动优化开关,X表示是优化等级
 -DABC=XYZ//定义常量,相当于原文件中定义#define const 
 
 -static //指示链接器构建一个完全链接的可执行程序,即链接静态库而不链接动态库  
 -shared //生成动态库,一般和上面的-fPIC一起使用
 -fPIC  //指示链接器创建一个共享的目标文件,即so文件
 -pedantic 如果使用了GCC对C语言的扩展,则提示一个告警信息
 
三、GCC对C标准的支持
 C语言的几个标准:
  *基于《C程序设计语言》此书的标准,也叫K&RC标准
  *ANSI C, 从1989年开始C语言被ANSI标准化,并扩充了K&RC标准,增加了标准库。
  *C99,随着C++标准化发展,对ANSI C进一步扩充,增加了许多新的特性,但是各个编译器对C99支持程度不一致。  
  参考:
  ) 
  
  GCC对C99的支持并不完全,如下地址中列出了GCC各版本对C99的支持情况及相关说明:
  
 
四、GCC对C标准的常用扩展
我们通常把这部分就叫作GNU C,在Linux内核代码中应用了大量的GCC扩展语法,下面是常用的一些扩展:
1)Statement Exprs:在复合语句中可以定义新的变量,并且把最后一条语句作为整个表达式的最后取值
2)Local Labels::可以定义局部标签,并且goto *&&lable则可以直接跳转到分枝执行代码
3)Nested Functions:支持函数的嵌套定义,这种函数只能在包含它的函数中使用,并且可以使用父函数中的信息
4)Constructing Calls:__builtin_XXX:函数系列,利用把父函数的参数压栈来达到使用同父函数相同的参数的函数调用。
5)typeof:得到表达式的数据类型
6)Conditionals::条件表达式可以不需要中间的表达式,x?:y==x?x:y
7)Long Long: 支持long long int定义
Complex: Data types for complex numbers.
Floating Types: Additional Floating Types.
Decimal Float: Decimal Floating Types.
Hex Floats: Hexadecimal floating-point constants.
Fixed-Point: Fixed-Point Types.
8)Zero Length:支持0长度的数组
9)Variable Length:可以定义可变长度的数组,数组的大小可以是一个变量的值
10)Empty Structures:支持定义一个空成员的结构体,结构体长度为0
11)Variadic Macros:支持用__VA_ARGS__作为函数的参数定义变长参的函数
Escaped Newlines: Slightly looser rules for escaped newlines
Subscripting: Any array can be subscripted, even if not an lvalue.
12)Pointer Arith:支持void和函数指针的加减法操作
Initializers: Non-constant initializers.
Compound Literals: Compound literals give structures, unions or arrays as values
Cast to Union: Casting to union type from any member of the union.
13)Case Ranges:支持case low ... high:范围操作。
14)Mixed Declarations:支持变量的定义和穿插在代码中间
15)Function Attributes:支持函数的__attribute__ 属性说明
16)Attribute Syntax: __attribute__的应用格式
17)Function Prototypes: 不支持函数的老式申明
18)C++ Comments:支持C++的//作为注释
19)Dollar Signs:"$"符号不可以作为标示符
20)Character Escapes:‘\e’表示
21)Variable Attributes:支持变量的__attribute__属性说明
22)Type Attributes:支持类型的__attribute__属性说明
23)Alignment:支持__alignof__得到类型或变量的对齐字节数
23)Inline:GCC在c99 inline的基础上进一步扩展
//下面4条为支持扩展汇编而引入
24)Extended Asm:支持以asm("")的扩展汇编
25)Constraints:支持扩展汇编用到的操作符约束
26)Asm Labels:指定C标式符在扩展汇编代码中的别名,asm/__asm__中引用
27)Explicit Reg Vars: 定义寄存器和变量进行绑定
28)Alternate Keywords:支持__打头的宏来替代常用扩展关键字,以避免-std=c99 -ansi 不认识,比如:__asm__,__inline__
29)Incomplete Enums:支持不完整的枚举类型定义,enum foo
30)__FUNCTION__:支持__FUNCTION__表示函数名称,在debug语句中和__FILE__,__LINE__一起使用
Return Address: Getting the return or frame address of a function.
Vector Extensions: Using vector instructions through built-in functions.
31)Offsetof: 通过__builtin_offsetof实现了offsetof宏
Atomic Builtins: Built-in functions for atomic memory access.
Object Size Checking: Built-in functions for limited buffer overflow checking.
32)Other Builtins: Other built-in functions.
比如分支预测,供编译器进行优化
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
Target Builtins: Built-in functions specific to particular targets.
Target Format Checks: Format checks specific to particular targets.
Pragmas: Pragmas accepted by GCC.
33)Unnamed Fields: 在枚举和结构体内支持未命名的枚举或者结构体成员,
34)Thread-Local: 用关键字__thread定义线程独有的变量数据,但需要静态链接器,动态链接器,libc.so,libpthread.so的支持
35)Binary constants: 支持二禁制书写,i = 0b101010/ i =      052;
 
以下几个扩展要重点介绍一下:
1)inline:
 stitic inline:函数为静态函数的inline形式,gcc会在其调用处将其汇编码展开编译而不为这个函数生成独立的汇编码。
 但除下面两种情况外不进行内联,一是存在通过函数指针进行调用的代码,二是函数是一个递归调用则也不能进行内联展开
 inline:内部函数调用此inline函数时进行内联,如果有外部调用此函数的时,则需要生成独立代码共外部调用
 extern inline:此种内联函数只会进行内联而不会生成独立汇编
 关于inline的扩展和c99有很大不同。
 参考资料:http://blog.csdn.net/rotus/archive/2009/07/01/4311962.aspx
 
2)__attribute__((ATTRIBUTE1,ATTRIBUTE2,...))
GNU C允许对函数,变量,类型进行属性申明,以手工形式对代码进行优化和更严格的检查。GNU C中支持大量的属性,下面是几个最常用到的:
 *noreturn:用来说明函数可以没有返回值,避免告警
 *unused:用来说明函数或者变量可能不会被使用,避免告警产生
 *section("section-name"):作用于函数或者变量,将函数或者变量存放在除.text/.data,.bss外的其他段中
 如:#define __init_call __attribute__ ((unused,__section__ (".initcall.init")))
 *aligned (ALIGNMENT):用于变量和结构体,共用体类型,告诉编译器按照指定的ALIGNMENT字节数进行对齐
 *packed:告诉编译器按照紧凑模式来存放数据,避免因自然对齐导致的空间浪费
 *always_inline:强制进行内联
 
3)GNU C内建了许多函数,以__builtin_打头的函数,比如:常量检测,__builtin_constant_p,判断函数调用地址:__builtin_return_address
 以及分支预测__builtin_expect,预扎取:,__builtin_prefetch(提高性能)
 
参考资料:
《Using the GNU Compiler Collection -For gcc version 4.4.2》

 
如下是网络上的一段大量应用到GNU C的代码,可以帮助加深对常用扩展的理解
#include
#include
#include
#define HW1 "Hello, world!\n"
#define HW2 "Hello, world!\nHello, world!\n"
#define PRINTIT(args...)    printf(args)
inline long
getnum(int argc, char *argv[]) __attribute__((always_inline));
int
main(int argc, char *argv[])
{
    __label__ retok, retfailed;
    int num = getnum(argc, argv);
    char buf[strlen(HW1)*num + 1];
    typeof(&buf[0]) formstr(char *buf, int n) {
        buf[0] = '\0';
        char *hw;
        switch (n & 1) {
            case 1 ... 1:
                hw = HW1;
                break;
            case 0 ... 0:
                n >>= 1;
                hw = HW2;
                break;
        }
        while (n-- > 0)
            strcat(buf, hw);
        return buf;
    }
    void *addr = &&retok;
    char *cp = formstr(buf, num) ? : ({addr = &&retfailed; "Failed\n";});
    PRINTIT(cp);
    goto *addr;
retfailed:
    return -1;
retok:
    return 0;
}
long
getnum(int argc, char *argv[])
{
    return ({long ret = 1;
            if (argc > 1) {
                ret = strtol(argv[1], (char**)NULL, 10);
                if (ret < 0 || ret == LONG_MAX || ret == LONG_MIN)
                    ret = 0;
            }
            ret;});
}  
 
五、GCC各部件
gcc安装的各个部件
部分             描述
c++         gcc的一个版本,默认语言设置为c++,而且在连续的时候自动包含标准c++库。这和g++一样
ccl          实际的c编译程序
cclplus         实际的c++编译程序
collect2        在不使用GNU链接程序的系统上,有必要运行collect2来产生特定的全局初始化代码(例如c++的构造函数和析构函数)
configure       GCC源代码树根目录中的一个脚本,用于设置值和创建GCC编译程序必须的make程序的描述文件
crt0.o         这个初始化和结束代码是为每个系统定制的,而且也被编译进该文件,该文件然后会被连接到每个可执行文件中来执行必要的启动和终止程序。
cygwinl.dll     windows的共享库提供的API,模拟UNIX系统调用。
f77          该驱动程序可用于编译Fortran
f77l         实际的Fortran编译程序。
g++          gcc的一个版本,默认语言设置为c++,而且在链接的时候自动包含标准c++库,这和c++一样
gcc          该驱动程序等同于执行编译程序和链接程序以产生必要的输出
gcj          该驱动程序用于编译java
gnatl         实际的Ada编译程序
gnatbind        一种工具,用于执行Ada语言的绑定
gnatlink        一种工具,用于执行Ada语言的绑定
jcl          实际的java编译程序
libgcc         该库包含的例程被作为编译程序的一部分,是因为它们可被链接到实际的可执行程序中。它们是特殊的例程,链接到可执行程序,来执行基本的任务,例如浮点运算。这些库中的例程通常都是平台相关的。
libgcj         运行时库包含所有的核心Java类
libobjc         对所有Objective-c程序都必须的运行时的库。
libstdc++       运行时库,包括定义为标准语言一部分的所有的c++类和函数   
阅读(2281) | 评论(0) | 转发(3) |
给主人留下些什么吧!~~