gcc_国产操作系统 - CSDN
gcc 订阅
GCC(GNU Compiler Collection,GNU编译器套件)是由GNU开发的编程语言译器。GNU编译器套件包括C、C++、 Objective-C、 Fortran、Java、Ada和Go语言前端,也包括了这些语言的库(如libstdc++,libgcj等。) [1]  GCC的初衷是为GNU操作系统专门编写的一款编译器。GNU系统是彻底的自由软件。此处,“自由”的含义是它尊重用户的自由 [2]  。 展开全文
GCC(GNU Compiler Collection,GNU编译器套件)是由GNU开发的编程语言译器。GNU编译器套件包括C、C++、 Objective-C、 Fortran、Java、Ada和Go语言前端,也包括了这些语言的库(如libstdc++,libgcj等。) [1]  GCC的初衷是为GNU操作系统专门编写的一款编译器。GNU系统是彻底的自由软件。此处,“自由”的含义是它尊重用户的自由 [2]  。
信息
软件授权
GNU通用公共许可证(GNU GPL)
软件名称
GNU Compiler Collection
更新时间
2019-02-22
编写语言
C/C++
软件版本
8.3.0
软件平台
类Unix操作系统
软件语言
多国语言
开发商
Free Software Foundation (自由软件基金会)
gcc简介
GCC是以GPL许可证所发行的自由软件,也是GNU计划的关键部分。GCC的初衷是为GNU操作系统专门编写一款编译器,现已被大多数类Unix操作系统(如Linux、BSD、MacOS X等)采纳为标准的编译器,甚至在微软的Windows上也可以使用GCC。GCC支持多种计算机体系结构芯片,如x86、ARM、MIPS等,并已被移植到其他多种硬件平台 [1]  。GCC原名为GNU C语言编译器(GNU C Compiler),只能处理C语言。但其很快扩展,变得可处理C++,后来又扩展为能够支持更多编程语言,如Fortran、Pascal、Objective -C、Java、Ada、Go以及各类处理器架构上的汇编语言等,所以改名GNU编译器套件(GNU Compiler Collection) [1]  。
收起全文
  • GCC

    千次阅读 2011-08-08 18:40:44
    Linux系统下的Gcc(GNU C Compiler)是GNU推出的功能强大、性能优越的多平台编译器,是GNU的代表作品之一。gcc是可以在多种硬体平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比平均效率要高20%~...
    Linux系统下的Gcc(GNU C Compiler)是GNU推出的功能强大、性能优越的多平台编译器,是GNU的代表作品之一。gcc是可以在多种硬体平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比平均效率要高20%~30%。

    Gcc编译器能将C、C++语言源程序、汇程式化序和目标程序编译、连接成可执行文件,如果没有给出可执行文件的名字,gcc将生成一个名为a.out的文件。

    在Linux系统中,可执行文件没有统一的后缀,系统从文件的属性来区分可执行文件和不可执行文件。

    而gcc则通过后缀来区别输入文件的类别,下面我们来介绍gcc所遵循的部分约定规则。

    .c为后缀的文件,C语言源代码文件;
    .a为后缀的文件,是由目标文件构成的档案库文件;
    .C,.cc或.cxx 为后缀的文件,是C++源代码文件;
    .h为后缀的文件,是程序所包含的头文件;
    .i 为后缀的文件,是已经预处理过的C源代码文件;
    .ii为后缀的文件,是已经预处理过的C++源代码文件;
    .m为后缀的文件,是Objective-C源代码文件;
    .o为后缀的文件,是编译后的目标文件;
    .s为后缀的文件,是汇编语言源代码文件;
    .S为后缀的文件,是经过预编译的汇编语言源代码文件。
    Gcc的执行过程
    虽然我们称Gcc是C语言的编译器,但使用gcc由C语言源代码文件生成可执行文件的过程不仅仅是编译的过程,而是要经历四个相互关联的步骤∶预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和连接(Linking)
    命令gcc首先调用cpp进行预处理,在预处理过程中,对源代码文件中的文件包含(include)、预编译语句(如宏定义define等)进行分析。接着调用cc1进行编译,这个阶段根据输入文件生成以.o为后缀的目标文件。汇编过程是针对汇编语言的步骤,调用as进行工作,一般来讲,.S为后缀的汇编语言源代码文件和汇编、.s为后缀的汇编语言文件经过预编译和汇编之后都生成以.o为后缀的目标文件。当所有的目标文件都生成之后,gcc就调用ld来完成最后的关键性工作,这个阶段就是连接。在连接阶段,所有的目标文件被安排在可执行程序中的恰当的位置,同时,该程序所调用到的库函数也从各自所在的档案库中连到合适的地方。

    Gcc的基本用法和选项
    在使用Gcc编译器的时候,我们必须给出一系列必要的调用参数和文件名称。Gcc编译器的调用参数大约有100多个,其中多数参数我们可能根本就用不到,这里只介绍其中最基本、最常用的参数。
    Gcc最基本的用法是∶gcc [options] [filenames]
    其中options就是编译器所需要的参数,filenames给出相关的文件名称。
    -c,只编译,不连接成为可执行文件,编译器只是由输入的.c等源代码文件生成.o为后缀的目标文件,通常用于编译不包含主程序的子程序文件。
    -o output_filename,确定输出文件的名称为output_filename,同时这个名称不能和源文件同名。如果不给出这个选项,gcc就给出预设的可执行文件a.out。
    -g,产生符号调试工具(GNU的gdb)所必要的符号资讯,要想对源代码进行调试,我们就必须加入这个选项。
    -O,对程序进行优化编译、连接,采用这个选项,整个源代码会在编译、连接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是,编译、连接的速度就相应地要慢一些。
    -O2,比-O更好的优化编译、连接,当然整个编译、连接过程会更慢。
    -Idirname,将dirname所指出的目录加入到程序头文件目录列表中,是在预编译过程中使用的参数。C程序中的头文件包含两种情况∶
    A)#include
    B)#include “myinc.h”
    其中,A类使用尖括号(< >),B类使用双引号(“ ”)。对于A类,预处理程序cpp在系统预设包含文件目录(如/usr/include)中搜寻相应的文件,而对于B类,cpp在当前目录中搜寻头文件,这个选项的作用是告诉cpp,如果在当前目录中没有找到需要的文件,就到指定的dirname目录中去寻找。在程序设计中,如果我们需要的这种包含文件分别分布在不同的目录中,就需要逐个使用-I选项给出搜索路径。
    -Ldirname,将dirname所指出的目录加入到程序函数档案库文件的目录列表中,是在连接过程中使用的参数。在预设状态下,连接程序ld在系统的预设路径中(如/usr/lib)寻找所需要的档案库文件,这个选项告诉连接程序,首先到-L指定的目录中去寻找,然后到系统预设路径中寻找,如果函数库存放在多个目录下,就需要依次使用这个选项,给出相应的存放目录。
    -lname,在连接时,装载名字为“libname.a”的函数库,该函数库位于系统预设的目录或者由-L选项确定的目录下。例如,-lm表示连接名为“libm.a”的数学函数库。
    上面我们简要介绍了gcc编译器最常用的功能和主要参数选项,更为详尽的资料可以参看Linux系统的联机帮助。
    假定我们有一个程序名为test.c的C语言源代码文件,要生成一个可执行文件,最简单的办法就是∶
    gcc test.c
    这时,预编译、编译连接一次完成,生成一个系统预设的名为a.out的可执行文件,对于稍为复杂的情况,比如有多个源代码文件、需要连接档案库或者有其他比较特别的要求,就要给定适当的调用选项参数。再看一个简单的例子。
    整个源代码程序由两个文件testmain.c 和testsub.c组成,程序中使用了系统提供的数学库,同时希望给出的可执行文件为test,这时的编译命令可以是∶
    gcc testmain.c testsub.c -lm -o test
    其中,-lm表示连接系统的数学库libm.a。

    Gcc的错误类型及对策
    Gcc编译器如果发现源程序中有错误,就无法继续进行,也无法生成最终的可执行文件。为了便于修改,gcc给出错误资讯,我们必须对这些错误资讯逐个进行分析、处理,并修改相应的语言,才能保证源代码的正确编译连接。gcc给出的错误资讯一般可以分为四大类,下面我们分别讨论其产生的原因和对策。

    第一类∶C语法错误
    错误资讯∶文件source.c中第n行有语法错误(syntex errror)。这种类型的错误,一般都是C语言的语法错误,应该仔细检查源代码文件中第n行及该行之前的程序,有时也需要对该文件所包含的头文件进行检查。有些情况下,一个很简单的语法错误,gcc会给出一大堆错误,我们最主要的是要保持清醒的头脑,不要被其吓倒,必要的时候再参考一下C语言的基本教材。
    第二类∶头文件错误
    错误资讯∶找不到头文件head.h(Can not find include file head.h)。这类错误是源代码文件中的包含头文件有问题,可能的原因有头文件名错误、指定的头文件所在目录名错误等,也可能是错误地使用了双引号和尖括号。
    第三类∶档案库错误
    错误资讯∶连接程序找不到所需的函数库,例如∶
    ld: -lm: No such file or directory
    这类错误是与目标文件相连接的函数库有错误,可能的原因是函数库名错误、指定的函数库所在目录名称错误等,检查的方法是使用find命令在可能的目录中寻找相应的函数库名,确定档案库及目录的名称并修改程序中及编译选项中的名称。
    第四类∶未定义符号
    错误资讯∶有未定义的符号(Undefined symbol)。这类错误是在连接过程中出现的,可能有两种原因∶一是使用者自己定义的函数或者全局变量所在源代码文件,没有被编译、连接,或者干脆还没有定义,这需要使用者根据实际情况修改源程序,给出全局变量或者函数的定义体;二是未定义的符号是一个标准的库函数,在源程序中使用了该库函数,而连接过程中还没有给定相应的函数库的名称,或者是该档案库的目录名称有问题,这时需要使用档案库维护命令ar检查我们需要的库函数到底位于哪一个函数库中,确定之后,修改gcc连接选项中的-l和-L项。
    排除编译、连接过程中的错误,应该说这只是程序设计中最简单、最基本的一个步骤,可以说只是开了个头。这个过程中的错误,只是我们在使用C语言描述一个算法中所产生的错误,是比较容易排除的。我们写一个程序,到编译、连接通过为止,应该说刚刚开始,程序在运行过程中所出现的问题,是算法设计有问题,说得更玄点是对问题的认识和理解不够,还需要更加深入地测试、调试和修改。一个程序,稍为复杂的程序,往往要经过多次的编译、连接和测试、修改。下面我们学习的程序维护、调试工具和版本维护就是在程序调试、测试过程中使用的,用来解决调测阶段所出现的问题。
    展开全文
  • GCC详解

    万次阅读 多人点赞 2017-08-25 16:49:24
    开放、自由和灵活是Linux的魅力所在,而这一点在gcc上的体现就是程序员通过它能够更好地控制整个编译过程。 在使用gcc编译程序时,编译过程可以细分为4个阶段: ● 预处理(Pre-Processing) ● 编译...

    开放、自由和灵活是Linux的魅力所在,而这一点在gcc上的体现就是程序员通过它能够更好地控制整个编译过程。

    在使用gcc编译程序时,编译过程可以细分为4个阶段:

    ●       预处理(Pre-Processing)

    ●       编译(Compiling)

    ●       汇编(Assembling)

    ●       链接(Linking)

    Linux程序员可以根据自己的需要让gcc在编译的任何阶段结束,检查或使用编译器在该阶段的输出信息,或者对最后生成的二进制文件进行控制,以便通过加入不同数量和种类的调试代码来为今后的调试做好准备。与其他常用的编译器一样,gcc也提供了灵活而强大的代码优化功能,利用它可以生成执行效率更高的代码。

    gcc提供了30多条警告信息和3个警告级别,使用它们有助于增强程序的稳定性和可移植性。此外,gcc还对标准的C和C++语言进行了大量的扩展,提高了程序的执行效率,有助于编译器进行代码优化,能够减轻编程的工作量。

    3.2  使 用 gcc

    gcc的版本可以使用如下gcc –v命令查看:

    [david@DAVID david]$ gcc -v

    Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/3.2.2/specs

    Configured with: ../configure --prefix=/usr --mandir=/usr/share/man

    --infodir=/

    sr/share/info --enable-shared --enable-threads=posix

    --disable-checking --with-

    ystem-zlib --enable-__cxa_atexit --host=i386-redhat-linux

    Thread model: posix

    gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5)

    以上显示的就是Redhat linux 9.0里自带的gcc的版本3.2.2。

    下面将以一个实例来说明如何使用gcc编译器。例3-1能够帮助大家迅速理解gcc的工作原理,并将其立即运用到实际的项目开发中去。

    实例3-1  hello.c­­­­­­­­­­­­­­­­­­­­­­­­­­­­

     


    #include <stdio.h>

    int main (int argc,char **argv) {

    printf("Hello Linux\n");

    }

    要编译这个程序,只要在命令行下执行如下命令:

    [david@DAVID david]$ gcc hello.c -o hello

    [david@DAVID david]$ ./hello

    Hello Linux

    这样,gcc 编译器会生成一个名为hello的可执行文件,然后执行./hello就可以看到程序的输出结果了。

    命令行中 gcc表示用gcc来编译源程序,-o 选项表示要求编译器输出的可执行文件名为hello ,而hello.c是源程序文件。从程序员的角度看,只需简单地执行一条gcc命令就可以了;但从编译器的角度来看,却需要完成一系列非常繁杂的工作。首先,gcc需要调用预处理程序cpp,由它负责展开在源文件中定义的宏,并向其中插入#include语句所包含的内容;接着,gcc会调用ccl和as将处理后的源代码编译成目标代码;最后,gcc会调用链接程序ld,把生成的目标代码链接成一个可执行程序。

    为了更好地理解gcc的工作过程,可以把上述编译过程分成几个步骤单独进行,并观察每步的运行结果。

    第一步要进行预编译,使用-E参数可以让gcc在预处理结束后停止编译过程:

    [david@DAVID david]$ gcc -E hello.c -o hello.i

    此时若查看hello.i文件中的内容,会发现stdio.h的内容确实都插到文件里去了,而且被预处理的宏定义也都作了相应的处理。

    # 1 "hello.c"

    # 1 "<built-in>"

    # 1 "<command line>"

    # 1 "hello.c"

    # 1 "/usr/include/stdio.h" 1 3

    # 28 "/usr/include/stdio.h" 3

    # 1 "/usr/include/features.h" 1 3

    # 291 "/usr/include/features.h" 3

    # 1 "/usr/include/sys/cdefs.h" 1 3

    # 292 "/usr/include/features.h" 2 3

    # 314 "/usr/include/features.h" 3

    # 1 "/usr/include/gnu/stubs.h" 1 3

    # 315 "/usr/include/features.h" 2 3

    # 29 "/usr/include/stdio.h" 2 3

    # 1 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/include/stddef.h" 1 3

    # 213 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/include/stddef.h" 3

    typedef unsigned int size_t;

    # 35 "/usr/include/stdio.h" 2 3

    # 1 "/usr/include/bits/types.h" 1 3

    # 28 "/usr/include/bits/types.h" 3

    # 1 "/usr/include/bits/wordsize.h" 1 3

    # 29 "/usr/include/bits/types.h" 2 3

    # 1 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/include/stddef.h" 1 3

    # 32 "/usr/include/bits/types.h" 2 3

     

    "hello.i" 838L, 16453C                         1,1           Top

    下一步是将hello.i编译为目标代码,这可以通过使用-c参数来完成:

    [david@DAVID david]$ gcc -c hello.i -o hello.o

    gcc默认将.i文件看成是预处理后的C语言源代码,因此上述命令将自动跳过预处理步骤而开始执行编译过程,也可以使用-x参数让gcc从指定的步骤开始编译。最后一步是将生成的目标文件链接成可执行文件:

    [david@DAVID david]$ gcc hello.o -o hello

    在采用模块化的设计思想进行软件开发时,通常整个程序是由多个源文件组成的,相应地就形成了多个编译单元,使用gcc能够很好地管理这些编译单元。假设有一个由david.c和xueer.c两个源文件组成的程序,为了对它们进行编译,并最终生成可执行程序davidxueer,可以使用下面这条命令:

    [david@DAVID david]$ gcc david.c xueer.c -o davidxueer

    如果同时处理的文件不止一个,gcc仍然会按照预处理、编译和链接的过程依次进行。如果深究起来,上面这条命令大致相当于依次执行如下3条命令:

    [david@DAVID david]$ gcc david.c -o david.o

    [david@DAVID david]$ gcc  xueer.c -o xueer.o

    [david@DAVID david]$ gcc david.o xueer.o -o davidxueer

    在编译一个包含许多源文件的工程时,若只用一条gcc命令来完成编译是非常浪费时间的。假设项目中有100个源文件需要编译,并且每个源文件中都包含10 000行代码,如果像上面那样仅用一条gcc命令来完成编译工作,那么gcc需要将每个源文件都重新编译一遍,然后再全部链接起来。很显然,这样浪费的时间相当多,尤其是当用户只是修改了其中某一个文件的时候,完全没有必要将每个文件都重新编译一遍,因为很多已经生成的目标文件是不会改变的。要解决这个问题,关键是要灵活运用gcc,同时还要借助像make这样的工具。关于make,将在第5章作详细的介绍。

    3.3  gcc警告提示功能

    gcc包含完整的出错检查和警告提示功能,它们可以帮助Linux程序员尽快找到错误代码,从而写出更加专业和优美的代码。先来读读例3-2所示的程序,这段代码写得很糟糕,仔细检查一下不难挑出如下毛病:

    ●       main函数的返回值被声明为void,但实际上应该是int;

    ●       使用了GNU语法扩展,即使用long long来声明64位整数,仍不符合ANSI/ISO C语言标准;

    ●       main函数在终止前没有调用return语句。

    实例3-2  bad.c­­­­­­­­­­­­­­­­­­­­­­­­­­­­

     


    #include <stdio.h>

    void main(void)

    {

      long long int var = 1;

      printf("It is not standard C code!\n");

    }

    下面看看gcc是如何帮助程序员来发现这些错误的。当gcc在编译不符合ANSI/ISO C语言标准的源代码时,如果加上了-pedantic选项,那么使用了扩展语法的地方将产生相应的警告信息:

    [david@DAVID david]$ gcc -pedantic bad.c -o bad

    bad.c: In function 'main':

    bad.c:4: warning: ISO C89 does not support 'long long'

    bad.c:3: warning: return type of 'main' is not 'int'

    需要注意的是,-pedantic编译选项并不能保证被编译程序与ANSI/ISO C标准的完全兼容,它仅仅用来帮助Linux程序员离这个目标越来越近。换句话说,-pedantic选项能够帮助程序员发现一些不符合ANSI/ISO C标准的代码,但不是全部。事实上只有ANSI/ISO C语言标准中要求进行编译器诊断的那些问题才有可能被gcc发现并提出警告。

    除了-pedantic之外,gcc还有一些其他编译选项也能够产生有用的警告信息。这些选项大多以-W开头,其中最有价值的当数-Wall了,使用它能够使gcc产生尽可能多的警告信息。例如:

    [david@DAVID david]$ gcc -Wall bad.c -o bad

    bad.c:3: warning: return type of 'main' is not 'int'

    bad.c: In function 'main':

    bad.c:4: warning: unused variable 'var'

    bad.c:6:2: warning: no newline at end of file

    gcc给出的警告信息虽然从严格意义上说不能算作是错误,但很可能成为错误的栖身之所。一个优秀的Linux程序员应该尽量避免产生警告信息,使自己的代码始终保持简洁、优美和健壮的特性。
        在处理警告方面,另一个常用的编译选项是-Werror,它要求gcc将所有的警告当成错误进行处理,这在使用自动编译工具(如make等)时非常有用。如果编译时带上-Werror选项,那么gcc会在所有产生警告的地方停止编译,迫使程序员对自己的代码进行修改。只有当相应的警告信息消除时,才可能将编译过程继续朝前推进。执行情况如下:

    [david@DAVID david]$ gcc -Werror bad.c -o bad

    cc1: warnings being treated as errors

    bad.c: In function 'main':

    bad.c:3: warning: return type of 'main' is not 'int'

    bad.c:6:2: no newline at end of file

    对Linux程序员来讲,gcc给出的警告信息是很有价值的,它们不仅可以帮助程序员写出更加健壮的程序,而且还是跟踪和调试程序的有力工具。建议在用gcc编译源代码时始终带上-Wall选项,并把它逐渐培养成为一种习惯,这对找出常见的隐式编程错误很有帮助。

    3.4  库  依  赖

    在Linux下使用C语言开发应用程序时,完全不使用第三方函数库的情况是比较少见的,通常来讲都需要借助一个或多个函数库的支持才能够完成相应的功能。从程序员的角度看,函数库实际上就是一些头文件(.h)和库文件(.so或者.a)的集合。虽然Linux下大多数函数都默认将头文件放到/usr/include/目录下,而库文件则放到/usr/lib/目录下,但并不是所有的情况都是这样。正因如此,gcc在编译时必须让编译器知道如何来查找所需要的头文件和库文件。

    gcc采用搜索目录的办法来查找所需要的文件,-I选项可以向gcc的头文件搜索路径中添加新的目录。例如,如果在/home/david/include/目录下有编译时所需要的头文件,为了让gcc能够顺利地找到它们,就可以使用-I选项:

    [david@DAVID david]$ gcc david.c -I /home/david/include -o david

    同样,如果使用了不在标准位置的库文件,那么可以通过-L选项向gcc的库文件搜索路径中添加新的目录。例如,如果在/home/david/lib/目录下有链接时所需要的库文件libdavid.so,为了让gcc能够顺利地找到它,可以使用下面的命令:

    [david@DAVID david]$ gcc david.c -L /home/david/lib –ldavid -o david

    值得详细解释一下的是-l选项,它指示gcc去连接库文件david.so。Linux下的库文件在命名时有一个约定,那就是应该以lib三个字母开头。由于所有的库文件都遵循了同样的规范,因此在用-l选项指定链接的库文件名时可以省去lib三个字母。也就是说gcc在对-l david进行处理时,会自动去链接名为libdavid.so的文件。

    Linux下的库文件分为两大类,分别是动态链接库(通常以.so结尾)和静态链接库(通常以.a结尾),两者的差别仅在于程序执行时所需的代码是在运行时动态加载的,还是在编译时静态加载的。默认情况下,gcc在链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库。如果需要的话可以在编译时加上-static选项,强制使用静态链接库。例如,如果在/home/david/lib/目录下有链接时所需要的库文件libfoo.so和libfoo.a,为了让gcc在链接时只用到静态链接库,可以使用下面的命令:

    [david@DAVID david]$ gcc foo.c -L /home/david/lib -static –ldavid -o

    david

    3.5  gcc代码优化

    代码优化指的是编译器通过分析源代码,找出其中尚未达到最优的部分,然后对其重新进行组合,目的是改善程序的执行性能。gcc提供的代码优化功能非常强大,它通过编译选项-On来控制优化代码的生成,其中n是一个代表优化级别的整数。对于不同版本的gcc来讲,n的取值范围及其对应的优化效果可能并不完全相同,比较典型的范围是从0变化到2或3。

    编译时使用选项-O可以告诉gcc同时减小代码的长度和执行时间,其效果等价于-O1。在这一级别上能够进行的优化类型虽然取决于目标处理器,但一般都会包括线程跳转(Thread Jump)和延迟退栈(Deferred Stack Pops)两种优化。

    选项-O2告诉gcc除了完成所有-O1级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令调度等。

    选项-O3则除了完成所有-O2级别的优化之外,还包括循环展开和其他一些与处理器特性相关的优化工作。

    通常来说,数字越大优化的等级越高,同时也就意味着程序的运行速度越快。许多Linux程序员都喜欢使用-O2选项,因为它在优化长度、编译时间和代码大小之间取得了一个比较理想的平衡点。

    下面通过具体实例来感受一下gcc的代码优化功能,所用程序如例3-3所示。

    实例3-3  count.c­­­­­­­­­­­­­­­­­­­­­­­­­­­­

     


    #include <stdio.h>

     int main(void)

    {  double counter;

       double result;

       double temp;

       for (counter = 0; counter < 4000.0 * 4000.0 * 4000.0  / 20.0 + 2030;   

    counter += (5 - 3 +2 + 1 ) / 4)

         {  temp = counter / 1239;

            result  = counter;   

           } 

       printf("Result is %lf\n", result); 

       return 0;

    }

    首先不加任何优化选项进行编译:

    [david@DAVID david]$ gcc -Wall count.c -o count

    借助Linux提供的time命令,可以大致统计出该程序在运行时所需要的时间:

    [david@DAVID david]$ time ./count

    Result is 3200002029.000000

    real    1m59.357s

    user    1m59.140s

    sys     0m0.050s

    接下来使用优化选项来对代码进行优化处理:

    [david@DAVID david]$ gcc -Wall count.c -o count2

    在同样的条件下再次测试一下运行时间:

    [david@DAVID david]$ time ./count2

    Result is 3200002029.000000

    real    0m26.573s

    user    0m26.540s

    sys     0m0.010s

    对比两次执行的输出结果不难看出,程序的性能的确得到了很大幅度的改善,由原来的1分59秒缩短到了26秒。这个例子是专门针对gcc的优化功能而设计的,因此优化前后程序的执行速度发生了很大的改变。尽管gcc的代码优化功能非常强大,但作为一名优秀的Linux程序员,首先还是要力求能够手工编写出高质量的代码。如果编写的代码简短,并且逻辑性强,编译器就不会做更多的工作,甚至根本用不着优化。

    优化虽然能够给程序带来更好的执行性能,但在如下一些场合中应该避免优化代码。

    ●       程序开发的时候:优化等级越高,消耗在编译上的时间就越长,因此在开发的时候最好不要使用优化选项,只有到软件发行或开发结束的时候,才考虑对最终生成的代码进行优化。

    ●       资源受限的时候:一些优化选项会增加可执行代码的体积,如果程序在运行时能够申请到的内存资源非常紧张(如一些实时嵌入式设备),那就不要对代码进行优化,因为由这带来的负面影响可能会产生非常严重的后果。

    ●       跟踪调试的时候:在对代码进行优化的时候,某些代码可能会被删除或改写,或者为了取得更佳的性能而进行重组,从而使跟踪和调试变得异常困难。

    3.6  加    速

    在将源代码变成可执行文件的过程中,需要经过许多中间步骤,包含预处理、编译、汇编和连接。这些过程实际上是由不同的程序负责完成的。大多数情况下gcc可以为Linux程序员完成所有的后台工作,自动调用相应程序进行处理。

    这样做有一个很明显的缺点,就是gcc在处理每一个源文件时,最终都需要生成好几个临时文件才能完成相应的工作,从而无形中导致处理速度变慢。例如,gcc在处理一个源文件时,可能需要一个临时文件来保存预处理的输出,一个临时文件来保存编译器的输出,一个临时文件来保存汇编器的输出,而读写这些临时文件显然需要耗费一定的时间。当软件项目变得非常庞大的时候,花费在这上面的代价可能会变得很大。

    解决的办法是,使用Linux提供的一种更加高效的通信方式—— 管道。它可以用来同时连接两个程序,其中一个程序的输出将直接作为另一个程序的输入,这样就可以避免使用临时文件,但编译时却需要消耗更多的内存。

    注意:

    在编译过程中使用管道是由gcc的-pipe选项决定的。下面的这条命令就是借助gcc的管道功能来提高编译速度的:

    [david@DAVID david]$ gcc -pipe david.c -o david

    在编译小型工程时使用管道,编译时间上的差异可能还不是很明显,但在源代码非常多的大型工程中,差异将变得非常明显。

    3.7  gcc常用选项

    gcc作为Linux下C/C++重要的编译环境,功能强大,编译选项繁多。为了方便大家日后编译方便,在此将常用的选项及说明罗列出来,见表3-2。

    表3-2  gcc的常用选项

    选  项  名

    作    用

    -c

    通知gcc取消连接步骤,即编译源码并在最后生成目标文件

    -Dmacro

    定义指定的宏,使它能够通过源码中的#ifdef进行检验

    -E

    不经过编译预处理程序的输出而输送至标准输出

    -g3

    获得有关调试程序的详细信息,它不能与-o选项联合使用

    -Idirectory

    在包含文件搜索路径的起点处添加指定目录

    -llibrary

    提示连接程序在创建最终可执行文件时包含指定的库

    -O、-O2、-O3

    将优化状态打开,该选项不能与-g选项联合使用

    -S

    要求编译程序生成来自源代码的汇编程序输出

    -v

    启动所有警报

    .h

    预处理文件(标头文件)

    -Wall

    在发生警报时取消编译操作,即将警报看作是错误

    -w

    禁止所有的报警

     

    3.8  gcc的错误类型及对策

    如果gcc编译器发现源程序中有错误,就无法继续进行,也无法生成最终的可执行文件。为了便于修改,gcc给出错误信息,必须对这些错误信息逐个进行分析、处理,并修改相应的源代码,才能保证源代码的正确编译连接。.gcc给出的错误信息一般可以分为四大类,下面我们分别讨论其产生的原因和对策。

    ●       第一类:C语法错误

    错误信息:文件source.c中第n行有语法错误(syntex errror)。这种类型的错误,一般都是C语言的语法错误,应该仔细检查源代码文件中第n行及该行之前的程序,有时也需要对该文件所包含的头文件进行检查。有些情况下,一个很简单的语法错误,gcc会给出一大堆错误,我们最主要的是要保持清醒的头脑,不要被其吓倒,必要的时候再参考一下C语言的基本教材。在这里推荐一本由Andrew Koenig写的《C 陷阱与缺陷》(此书已由人民邮电出版社翻译出版),说得夸张一点就是此书可以帮助你减少C代码和初级C++代码中的90%的bug。

    ●       第二类:头文件错误

    错误信息:找不到头文件head.h(Can not find include file head.h)。这类错误是源代码文件中包含的头文件有问题,可能的原因有头文件名错误、指定的头文件所在目录名错误等,也可能是错误地使用了双引号和尖括号。

    ●       第三类:档案库错误

    错误信息:连接程序找不到所需的函数库,例如:

    ld: -lm: No such file or directory

    这类错误是与目标文件相连接的函数库有错误,可能的原因是函数库名错误、指定的函数库所在目录名称错误等。检查的方法是使用find命令在可能的目录中寻找相应的函数库名,确定档案库及目录的名称并修改程序中及编译选项中的名称。

    ●       第四类:未定义符号

    错误信息:有未定义的符号(Undefined symbol)。这类错误是在连接过程中出现的,可能有两种原因:一是用户自己定义的函数或者全局变量所在源代码文件,没有被编译、连接,或者干脆还没有定义,这需要用户根据实际情况修改源程序,给出全局变量或者函数的定义体;二是未定义的符号是一个标准的库函数,在源程序中使用了该库函数,而连接过程中还没有给定相应的函数库的名称,或者是该档案库的目录名称有问题,这时需要使用档案库维护命令ar检查我们需要的库函数到底位于哪一个函数库中,确定之后,修改gcc连接选项中的-l和-L项。

    排除编译、连接过程中的错误,应该说只是程序设计中最简单、最基本的一个步骤,可以说只是开了个头。这个过程中的错误,只是我们在使用C语言描述一个算法中所产生的错误,是比较容易排除的。我们写一个程序,到编译、连接通过为止,应该说刚刚开始,程序在运行过程中所出现的问题,是算法设计有问题,说得严重点儿是对问题的认识和理解不够,还需要更加深入地测试、调试和修改。一个程序,稍为复杂的程序,往往要经过多次的编译、连接、测试和修改。 gcc是在Linux下开发程序时必须掌握的工具之一。

    以上对gcc作了一个简要的介绍,主要讲述了如何使用gcc编译程序、产生警告信息、和加快gcc的编译速度。对所有希望早日跨入Linux开发者行列的人来说,gcc就是成为一名优秀的Linux程序员的起跑线。关于调试 C 程序的更多信息请看第4章关于gdb的内容。


    开放、自由和灵活是Linux的魅力所在,而这一点在gcc上的体现就是程序员通过它能够更好地控制整个编译过程。

    在使用gcc编译程序时,编译过程可以细分为4个阶段:

    ●       预处理(Pre-Processing)

    ●       编译(Compiling)

    ●       汇编(Assembling)

    ●       链接(Linking)

    Linux程序员可以根据自己的需要让gcc在编译的任何阶段结束,检查或使用编译器在该阶段的输出信息,或者对最后生成的二进制文件进行控制,以便通过加入不同数量和种类的调试代码来为今后的调试做好准备。与其他常用的编译器一样,gcc也提供了灵活而强大的代码优化功能,利用它可以生成执行效率更高的代码。

    gcc提供了30多条警告信息和3个警告级别,使用它们有助于增强程序的稳定性和可移植性。此外,gcc还对标准的C和C++语言进行了大量的扩展,提高了程序的执行效率,有助于编译器进行代码优化,能够减轻编程的工作量。

    3.2  使 用 gcc

    gcc的版本可以使用如下gcc –v命令查看:

    [david@DAVID david]$ gcc -v

    Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/3.2.2/specs

    Configured with: ../configure --prefix=/usr --mandir=/usr/share/man

    --infodir=/

    sr/share/info --enable-shared --enable-threads=posix

    --disable-checking --with-

    ystem-zlib --enable-__cxa_atexit --host=i386-redhat-linux

    Thread model: posix

    gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5)

    以上显示的就是Redhat linux 9.0里自带的gcc的版本3.2.2。

    下面将以一个实例来说明如何使用gcc编译器。例3-1能够帮助大家迅速理解gcc的工作原理,并将其立即运用到实际的项目开发中去。

    实例3-1  hello.c­­­­­­­­­­­­­­­­­­­­­­­­­­­­

     


    #include <stdio.h>

    int main (int argc,char **argv) {

    printf("Hello Linux\n");

    }

    要编译这个程序,只要在命令行下执行如下命令:

    [david@DAVID david]$ gcc hello.c -o hello

    [david@DAVID david]$ ./hello

    Hello Linux

    这样,gcc 编译器会生成一个名为hello的可执行文件,然后执行./hello就可以看到程序的输出结果了。

    命令行中 gcc表示用gcc来编译源程序,-o 选项表示要求编译器输出的可执行文件名为hello ,而hello.c是源程序文件。从程序员的角度看,只需简单地执行一条gcc命令就可以了;但从编译器的角度来看,却需要完成一系列非常繁杂的工作。首先,gcc需要调用预处理程序cpp,由它负责展开在源文件中定义的宏,并向其中插入#include语句所包含的内容;接着,gcc会调用ccl和as将处理后的源代码编译成目标代码;最后,gcc会调用链接程序ld,把生成的目标代码链接成一个可执行程序。

    为了更好地理解gcc的工作过程,可以把上述编译过程分成几个步骤单独进行,并观察每步的运行结果。

    第一步要进行预编译,使用-E参数可以让gcc在预处理结束后停止编译过程:

    [david@DAVID david]$ gcc -E hello.c -o hello.i

    此时若查看hello.i文件中的内容,会发现stdio.h的内容确实都插到文件里去了,而且被预处理的宏定义也都作了相应的处理。

    # 1 "hello.c"

    # 1 "<built-in>"

    # 1 "<command line>"

    # 1 "hello.c"

    # 1 "/usr/include/stdio.h" 1 3

    # 28 "/usr/include/stdio.h" 3

    # 1 "/usr/include/features.h" 1 3

    # 291 "/usr/include/features.h" 3

    # 1 "/usr/include/sys/cdefs.h" 1 3

    # 292 "/usr/include/features.h" 2 3

    # 314 "/usr/include/features.h" 3

    # 1 "/usr/include/gnu/stubs.h" 1 3

    # 315 "/usr/include/features.h" 2 3

    # 29 "/usr/include/stdio.h" 2 3

    # 1 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/include/stddef.h" 1 3

    # 213 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/include/stddef.h" 3

    typedef unsigned int size_t;

    # 35 "/usr/include/stdio.h" 2 3

    # 1 "/usr/include/bits/types.h" 1 3

    # 28 "/usr/include/bits/types.h" 3

    # 1 "/usr/include/bits/wordsize.h" 1 3

    # 29 "/usr/include/bits/types.h" 2 3

    # 1 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/include/stddef.h" 1 3

    # 32 "/usr/include/bits/types.h" 2 3

     

    "hello.i" 838L, 16453C                         1,1           Top

    下一步是将hello.i编译为目标代码,这可以通过使用-c参数来完成:

    [david@DAVID david]$ gcc -c hello.i -o hello.o

    gcc默认将.i文件看成是预处理后的C语言源代码,因此上述命令将自动跳过预处理步骤而开始执行编译过程,也可以使用-x参数让gcc从指定的步骤开始编译。最后一步是将生成的目标文件链接成可执行文件:

    [david@DAVID david]$ gcc hello.o -o hello

    在采用模块化的设计思想进行软件开发时,通常整个程序是由多个源文件组成的,相应地就形成了多个编译单元,使用gcc能够很好地管理这些编译单元。假设有一个由david.c和xueer.c两个源文件组成的程序,为了对它们进行编译,并最终生成可执行程序davidxueer,可以使用下面这条命令:

    [david@DAVID david]$ gcc david.c xueer.c -o davidxueer

    如果同时处理的文件不止一个,gcc仍然会按照预处理、编译和链接的过程依次进行。如果深究起来,上面这条命令大致相当于依次执行如下3条命令:

    [david@DAVID david]$ gcc david.c -o david.o

    [david@DAVID david]$ gcc  xueer.c -o xueer.o

    [david@DAVID david]$ gcc david.o xueer.o -o davidxueer

    在编译一个包含许多源文件的工程时,若只用一条gcc命令来完成编译是非常浪费时间的。假设项目中有100个源文件需要编译,并且每个源文件中都包含10 000行代码,如果像上面那样仅用一条gcc命令来完成编译工作,那么gcc需要将每个源文件都重新编译一遍,然后再全部链接起来。很显然,这样浪费的时间相当多,尤其是当用户只是修改了其中某一个文件的时候,完全没有必要将每个文件都重新编译一遍,因为很多已经生成的目标文件是不会改变的。要解决这个问题,关键是要灵活运用gcc,同时还要借助像make这样的工具。关于make,将在第5章作详细的介绍。

    3.3  gcc警告提示功能

    gcc包含完整的出错检查和警告提示功能,它们可以帮助Linux程序员尽快找到错误代码,从而写出更加专业和优美的代码。先来读读例3-2所示的程序,这段代码写得很糟糕,仔细检查一下不难挑出如下毛病:

    ●       main函数的返回值被声明为void,但实际上应该是int;

    ●       使用了GNU语法扩展,即使用long long来声明64位整数,仍不符合ANSI/ISO C语言标准;

    ●       main函数在终止前没有调用return语句。

    实例3-2  bad.c­­­­­­­­­­­­­­­­­­­­­­­­­­­­

     


    #include <stdio.h>

    void main(void)

    {

      long long int var = 1;

      printf("It is not standard C code!\n");

    }

    下面看看gcc是如何帮助程序员来发现这些错误的。当gcc在编译不符合ANSI/ISO C语言标准的源代码时,如果加上了-pedantic选项,那么使用了扩展语法的地方将产生相应的警告信息:

    [david@DAVID david]$ gcc -pedantic bad.c -o bad

    bad.c: In function 'main':

    bad.c:4: warning: ISO C89 does not support 'long long'

    bad.c:3: warning: return type of 'main' is not 'int'

    需要注意的是,-pedantic编译选项并不能保证被编译程序与ANSI/ISO C标准的完全兼容,它仅仅用来帮助Linux程序员离这个目标越来越近。换句话说,-pedantic选项能够帮助程序员发现一些不符合ANSI/ISO C标准的代码,但不是全部。事实上只有ANSI/ISO C语言标准中要求进行编译器诊断的那些问题才有可能被gcc发现并提出警告。

    除了-pedantic之外,gcc还有一些其他编译选项也能够产生有用的警告信息。这些选项大多以-W开头,其中最有价值的当数-Wall了,使用它能够使gcc产生尽可能多的警告信息。例如:

    [david@DAVID david]$ gcc -Wall bad.c -o bad

    bad.c:3: warning: return type of 'main' is not 'int'

    bad.c: In function 'main':

    bad.c:4: warning: unused variable 'var'

    bad.c:6:2: warning: no newline at end of file

    gcc给出的警告信息虽然从严格意义上说不能算作是错误,但很可能成为错误的栖身之所。一个优秀的Linux程序员应该尽量避免产生警告信息,使自己的代码始终保持简洁、优美和健壮的特性。
        在处理警告方面,另一个常用的编译选项是-Werror,它要求gcc将所有的警告当成错误进行处理,这在使用自动编译工具(如make等)时非常有用。如果编译时带上-Werror选项,那么gcc会在所有产生警告的地方停止编译,迫使程序员对自己的代码进行修改。只有当相应的警告信息消除时,才可能将编译过程继续朝前推进。执行情况如下:

    [david@DAVID david]$ gcc -Werror bad.c -o bad

    cc1: warnings being treated as errors

    bad.c: In function 'main':

    bad.c:3: warning: return type of 'main' is not 'int'

    bad.c:6:2: no newline at end of file

    对Linux程序员来讲,gcc给出的警告信息是很有价值的,它们不仅可以帮助程序员写出更加健壮的程序,而且还是跟踪和调试程序的有力工具。建议在用gcc编译源代码时始终带上-Wall选项,并把它逐渐培养成为一种习惯,这对找出常见的隐式编程错误很有帮助。

    3.4  库  依  赖

    在Linux下使用C语言开发应用程序时,完全不使用第三方函数库的情况是比较少见的,通常来讲都需要借助一个或多个函数库的支持才能够完成相应的功能。从程序员的角度看,函数库实际上就是一些头文件(.h)和库文件(.so或者.a)的集合。虽然Linux下大多数函数都默认将头文件放到/usr/include/目录下,而库文件则放到/usr/lib/目录下,但并不是所有的情况都是这样。正因如此,gcc在编译时必须让编译器知道如何来查找所需要的头文件和库文件。

    gcc采用搜索目录的办法来查找所需要的文件,-I选项可以向gcc的头文件搜索路径中添加新的目录。例如,如果在/home/david/include/目录下有编译时所需要的头文件,为了让gcc能够顺利地找到它们,就可以使用-I选项:

    [david@DAVID david]$ gcc david.c -I /home/david/include -o david

    同样,如果使用了不在标准位置的库文件,那么可以通过-L选项向gcc的库文件搜索路径中添加新的目录。例如,如果在/home/david/lib/目录下有链接时所需要的库文件libdavid.so,为了让gcc能够顺利地找到它,可以使用下面的命令:

    [david@DAVID david]$ gcc david.c -L /home/david/lib –ldavid -o david

    值得详细解释一下的是-l选项,它指示gcc去连接库文件david.so。Linux下的库文件在命名时有一个约定,那就是应该以lib三个字母开头。由于所有的库文件都遵循了同样的规范,因此在用-l选项指定链接的库文件名时可以省去lib三个字母。也就是说gcc在对-l david进行处理时,会自动去链接名为libdavid.so的文件。

    Linux下的库文件分为两大类,分别是动态链接库(通常以.so结尾)和静态链接库(通常以.a结尾),两者的差别仅在于程序执行时所需的代码是在运行时动态加载的,还是在编译时静态加载的。默认情况下,gcc在链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库。如果需要的话可以在编译时加上-static选项,强制使用静态链接库。例如,如果在/home/david/lib/目录下有链接时所需要的库文件libfoo.so和libfoo.a,为了让gcc在链接时只用到静态链接库,可以使用下面的命令:

    [david@DAVID david]$ gcc foo.c -L /home/david/lib -static –ldavid -o

    david

    3.5  gcc代码优化

    代码优化指的是编译器通过分析源代码,找出其中尚未达到最优的部分,然后对其重新进行组合,目的是改善程序的执行性能。gcc提供的代码优化功能非常强大,它通过编译选项-On来控制优化代码的生成,其中n是一个代表优化级别的整数。对于不同版本的gcc来讲,n的取值范围及其对应的优化效果可能并不完全相同,比较典型的范围是从0变化到2或3。

    编译时使用选项-O可以告诉gcc同时减小代码的长度和执行时间,其效果等价于-O1。在这一级别上能够进行的优化类型虽然取决于目标处理器,但一般都会包括线程跳转(Thread Jump)和延迟退栈(Deferred Stack Pops)两种优化。

    选项-O2告诉gcc除了完成所有-O1级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令调度等。

    选项-O3则除了完成所有-O2级别的优化之外,还包括循环展开和其他一些与处理器特性相关的优化工作。

    通常来说,数字越大优化的等级越高,同时也就意味着程序的运行速度越快。许多Linux程序员都喜欢使用-O2选项,因为它在优化长度、编译时间和代码大小之间取得了一个比较理想的平衡点。

    下面通过具体实例来感受一下gcc的代码优化功能,所用程序如例3-3所示。

    实例3-3  count.c­­­­­­­­­­­­­­­­­­­­­­­­­­­­

     


    #include <stdio.h>

     int main(void)

    {  double counter;

       double result;

       double temp;

       for (counter = 0; counter < 4000.0 * 4000.0 * 4000.0  / 20.0 + 2030;   

    counter += (5 - 3 +2 + 1 ) / 4)

         {  temp = counter / 1239;

            result  = counter;   

           } 

       printf("Result is %lf\n", result); 

       return 0;

    }

    首先不加任何优化选项进行编译:

    [david@DAVID david]$ gcc -Wall count.c -o count

    借助Linux提供的time命令,可以大致统计出该程序在运行时所需要的时间:

    [david@DAVID david]$ time ./count

    Result is 3200002029.000000

    real    1m59.357s

    user    1m59.140s

    sys     0m0.050s

    接下来使用优化选项来对代码进行优化处理:

    [david@DAVID david]$ gcc -Wall count.c -o count2

    在同样的条件下再次测试一下运行时间:

    [david@DAVID david]$ time ./count2

    Result is 3200002029.000000

    real    0m26.573s

    user    0m26.540s

    sys     0m0.010s

    对比两次执行的输出结果不难看出,程序的性能的确得到了很大幅度的改善,由原来的1分59秒缩短到了26秒。这个例子是专门针对gcc的优化功能而设计的,因此优化前后程序的执行速度发生了很大的改变。尽管gcc的代码优化功能非常强大,但作为一名优秀的Linux程序员,首先还是要力求能够手工编写出高质量的代码。如果编写的代码简短,并且逻辑性强,编译器就不会做更多的工作,甚至根本用不着优化。

    优化虽然能够给程序带来更好的执行性能,但在如下一些场合中应该避免优化代码。

    ●       程序开发的时候:优化等级越高,消耗在编译上的时间就越长,因此在开发的时候最好不要使用优化选项,只有到软件发行或开发结束的时候,才考虑对最终生成的代码进行优化。

    ●       资源受限的时候:一些优化选项会增加可执行代码的体积,如果程序在运行时能够申请到的内存资源非常紧张(如一些实时嵌入式设备),那就不要对代码进行优化,因为由这带来的负面影响可能会产生非常严重的后果。

    ●       跟踪调试的时候:在对代码进行优化的时候,某些代码可能会被删除或改写,或者为了取得更佳的性能而进行重组,从而使跟踪和调试变得异常困难。

    3.6  加    速

    在将源代码变成可执行文件的过程中,需要经过许多中间步骤,包含预处理、编译、汇编和连接。这些过程实际上是由不同的程序负责完成的。大多数情况下gcc可以为Linux程序员完成所有的后台工作,自动调用相应程序进行处理。

    这样做有一个很明显的缺点,就是gcc在处理每一个源文件时,最终都需要生成好几个临时文件才能完成相应的工作,从而无形中导致处理速度变慢。例如,gcc在处理一个源文件时,可能需要一个临时文件来保存预处理的输出,一个临时文件来保存编译器的输出,一个临时文件来保存汇编器的输出,而读写这些临时文件显然需要耗费一定的时间。当软件项目变得非常庞大的时候,花费在这上面的代价可能会变得很大。

    解决的办法是,使用Linux提供的一种更加高效的通信方式—— 管道。它可以用来同时连接两个程序,其中一个程序的输出将直接作为另一个程序的输入,这样就可以避免使用临时文件,但编译时却需要消耗更多的内存。

    注意:

    在编译过程中使用管道是由gcc的-pipe选项决定的。下面的这条命令就是借助gcc的管道功能来提高编译速度的:

    [david@DAVID david]$ gcc -pipe david.c -o david

    在编译小型工程时使用管道,编译时间上的差异可能还不是很明显,但在源代码非常多的大型工程中,差异将变得非常明显。

    3.7  gcc常用选项

    gcc作为Linux下C/C++重要的编译环境,功能强大,编译选项繁多。为了方便大家日后编译方便,在此将常用的选项及说明罗列出来,见表3-2。

    表3-2  gcc的常用选项

    选  项  名

    作    用

    -c

    通知gcc取消连接步骤,即编译源码并在最后生成目标文件

    -Dmacro

    定义指定的宏,使它能够通过源码中的#ifdef进行检验

    -E

    不经过编译预处理程序的输出而输送至标准输出

    -g3

    获得有关调试程序的详细信息,它不能与-o选项联合使用

    -Idirectory

    在包含文件搜索路径的起点处添加指定目录

    -llibrary

    提示连接程序在创建最终可执行文件时包含指定的库

    -O、-O2、-O3

    将优化状态打开,该选项不能与-g选项联合使用

    -S

    要求编译程序生成来自源代码的汇编程序输出

    -v

    启动所有警报

    .h

    预处理文件(标头文件)

    -Wall

    在发生警报时取消编译操作,即将警报看作是错误

    -w

    禁止所有的报警

     

    3.8  gcc的错误类型及对策

    如果gcc编译器发现源程序中有错误,就无法继续进行,也无法生成最终的可执行文件。为了便于修改,gcc给出错误信息,必须对这些错误信息逐个进行分析、处理,并修改相应的源代码,才能保证源代码的正确编译连接。.gcc给出的错误信息一般可以分为四大类,下面我们分别讨论其产生的原因和对策。

    ●       第一类:C语法错误

    错误信息:文件source.c中第n行有语法错误(syntex errror)。这种类型的错误,一般都是C语言的语法错误,应该仔细检查源代码文件中第n行及该行之前的程序,有时也需要对该文件所包含的头文件进行检查。有些情况下,一个很简单的语法错误,gcc会给出一大堆错误,我们最主要的是要保持清醒的头脑,不要被其吓倒,必要的时候再参考一下C语言的基本教材。在这里推荐一本由Andrew Koenig写的《C 陷阱与缺陷》(此书已由人民邮电出版社翻译出版),说得夸张一点就是此书可以帮助你减少C代码和初级C++代码中的90%的bug。

    ●       第二类:头文件错误

    错误信息:找不到头文件head.h(Can not find include file head.h)。这类错误是源代码文件中包含的头文件有问题,可能的原因有头文件名错误、指定的头文件所在目录名错误等,也可能是错误地使用了双引号和尖括号。

    ●       第三类:档案库错误

    错误信息:连接程序找不到所需的函数库,例如:

    ld: -lm: No such file or directory

    这类错误是与目标文件相连接的函数库有错误,可能的原因是函数库名错误、指定的函数库所在目录名称错误等。检查的方法是使用find命令在可能的目录中寻找相应的函数库名,确定档案库及目录的名称并修改程序中及编译选项中的名称。

    ●       第四类:未定义符号

    错误信息:有未定义的符号(Undefined symbol)。这类错误是在连接过程中出现的,可能有两种原因:一是用户自己定义的函数或者全局变量所在源代码文件,没有被编译、连接,或者干脆还没有定义,这需要用户根据实际情况修改源程序,给出全局变量或者函数的定义体;二是未定义的符号是一个标准的库函数,在源程序中使用了该库函数,而连接过程中还没有给定相应的函数库的名称,或者是该档案库的目录名称有问题,这时需要使用档案库维护命令ar检查我们需要的库函数到底位于哪一个函数库中,确定之后,修改gcc连接选项中的-l和-L项。

    排除编译、连接过程中的错误,应该说只是程序设计中最简单、最基本的一个步骤,可以说只是开了个头。这个过程中的错误,只是我们在使用C语言描述一个算法中所产生的错误,是比较容易排除的。我们写一个程序,到编译、连接通过为止,应该说刚刚开始,程序在运行过程中所出现的问题,是算法设计有问题,说得严重点儿是对问题的认识和理解不够,还需要更加深入地测试、调试和修改。一个程序,稍为复杂的程序,往往要经过多次的编译、连接、测试和修改。 gcc是在Linux下开发程序时必须掌握的工具之一。

    以上对gcc作了一个简要的介绍,主要讲述了如何使用gcc编译程序、产生警告信息、和加快gcc的编译速度。对所有希望早日跨入Linux开发者行列的人来说,gcc就是成为一名优秀的Linux程序员的起跑线。关于调试 C 程序的更多信息请看第4章关于gdb的内容。


    开放、自由和灵活是Linux的魅力所在,而这一点在gcc上的体现就是程序员通过它能够更好地控制整个编译过程。

    在使用gcc编译程序时,编译过程可以细分为4个阶段:

    ●       预处理(Pre-Processing)

    ●       编译(Compiling)

    ●       汇编(Assembling)

    ●       链接(Linking)

    Linux程序员可以根据自己的需要让gcc在编译的任何阶段结束,检查或使用编译器在该阶段的输出信息,或者对最后生成的二进制文件进行控制,以便通过加入不同数量和种类的调试代码来为今后的调试做好准备。与其他常用的编译器一样,gcc也提供了灵活而强大的代码优化功能,利用它可以生成执行效率更高的代码。

    gcc提供了30多条警告信息和3个警告级别,使用它们有助于增强程序的稳定性和可移植性。此外,gcc还对标准的C和C++语言进行了大量的扩展,提高了程序的执行效率,有助于编译器进行代码优化,能够减轻编程的工作量。

    3.2  使 用 gcc

    gcc的版本可以使用如下gcc –v命令查看:

    [david@DAVID david]$ gcc -v

    Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/3.2.2/specs

    Configured with: ../configure --prefix=/usr --mandir=/usr/share/man

    --infodir=/

    sr/share/info --enable-shared --enable-threads=posix

    --disable-checking --with-

    ystem-zlib --enable-__cxa_atexit --host=i386-redhat-linux

    Thread model: posix

    gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5)

    以上显示的就是Redhat linux 9.0里自带的gcc的版本3.2.2。

    下面将以一个实例来说明如何使用gcc编译器。例3-1能够帮助大家迅速理解gcc的工作原理,并将其立即运用到实际的项目开发中去。

    实例3-1  hello.c­­­­­­­­­­­­­­­­­­­­­­­­­­­­

     


    #include <stdio.h>

    int main (int argc,char **argv) {

    printf("Hello Linux\n");

    }

    要编译这个程序,只要在命令行下执行如下命令:

    [david@DAVID david]$ gcc hello.c -o hello

    [david@DAVID david]$ ./hello

    Hello Linux

    这样,gcc 编译器会生成一个名为hello的可执行文件,然后执行./hello就可以看到程序的输出结果了。

    命令行中 gcc表示用gcc来编译源程序,-o 选项表示要求编译器输出的可执行文件名为hello ,而hello.c是源程序文件。从程序员的角度看,只需简单地执行一条gcc命令就可以了;但从编译器的角度来看,却需要完成一系列非常繁杂的工作。首先,gcc需要调用预处理程序cpp,由它负责展开在源文件中定义的宏,并向其中插入#include语句所包含的内容;接着,gcc会调用ccl和as将处理后的源代码编译成目标代码;最后,gcc会调用链接程序ld,把生成的目标代码链接成一个可执行程序。

    为了更好地理解gcc的工作过程,可以把上述编译过程分成几个步骤单独进行,并观察每步的运行结果。

    第一步要进行预编译,使用-E参数可以让gcc在预处理结束后停止编译过程:

    [david@DAVID david]$ gcc -E hello.c -o hello.i

    此时若查看hello.i文件中的内容,会发现stdio.h的内容确实都插到文件里去了,而且被预处理的宏定义也都作了相应的处理。

    # 1 "hello.c"

    # 1 "<built-in>"

    # 1 "<command line>"

    # 1 "hello.c"

    # 1 "/usr/include/stdio.h" 1 3

    # 28 "/usr/include/stdio.h" 3

    # 1 "/usr/include/features.h" 1 3

    # 291 "/usr/include/features.h" 3

    # 1 "/usr/include/sys/cdefs.h" 1 3

    # 292 "/usr/include/features.h" 2 3

    # 314 "/usr/include/features.h" 3

    # 1 "/usr/include/gnu/stubs.h" 1 3

    # 315 "/usr/include/features.h" 2 3

    # 29 "/usr/include/stdio.h" 2 3

    # 1 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/include/stddef.h" 1 3

    # 213 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/include/stddef.h" 3

    typedef unsigned int size_t;

    # 35 "/usr/include/stdio.h" 2 3

    # 1 "/usr/include/bits/types.h" 1 3

    # 28 "/usr/include/bits/types.h" 3

    # 1 "/usr/include/bits/wordsize.h" 1 3

    # 29 "/usr/include/bits/types.h" 2 3

    # 1 "/usr/lib/gcc-lib/i386-redhat-linux/3.2.2/include/stddef.h" 1 3

    # 32 "/usr/include/bits/types.h" 2 3

     

    "hello.i" 838L, 16453C                         1,1           Top

    下一步是将hello.i编译为目标代码,这可以通过使用-c参数来完成:

    [david@DAVID david]$ gcc -c hello.i -o hello.o

    gcc默认将.i文件看成是预处理后的C语言源代码,因此上述命令将自动跳过预处理步骤而开始执行编译过程,也可以使用-x参数让gcc从指定的步骤开始编译。最后一步是将生成的目标文件链接成可执行文件:

    [david@DAVID david]$ gcc hello.o -o hello

    在采用模块化的设计思想进行软件开发时,通常整个程序是由多个源文件组成的,相应地就形成了多个编译单元,使用gcc能够很好地管理这些编译单元。假设有一个由david.c和xueer.c两个源文件组成的程序,为了对它们进行编译,并最终生成可执行程序davidxueer,可以使用下面这条命令:

    [david@DAVID david]$ gcc david.c xueer.c -o davidxueer

    如果同时处理的文件不止一个,gcc仍然会按照预处理、编译和链接的过程依次进行。如果深究起来,上面这条命令大致相当于依次执行如下3条命令:

    [david@DAVID david]$ gcc david.c -o david.o

    [david@DAVID david]$ gcc  xueer.c -o xueer.o

    [david@DAVID david]$ gcc david.o xueer.o -o davidxueer

    在编译一个包含许多源文件的工程时,若只用一条gcc命令来完成编译是非常浪费时间的。假设项目中有100个源文件需要编译,并且每个源文件中都包含10 000行代码,如果像上面那样仅用一条gcc命令来完成编译工作,那么gcc需要将每个源文件都重新编译一遍,然后再全部链接起来。很显然,这样浪费的时间相当多,尤其是当用户只是修改了其中某一个文件的时候,完全没有必要将每个文件都重新编译一遍,因为很多已经生成的目标文件是不会改变的。要解决这个问题,关键是要灵活运用gcc,同时还要借助像make这样的工具。关于make,将在第5章作详细的介绍。

    3.3  gcc警告提示功能

    gcc包含完整的出错检查和警告提示功能,它们可以帮助Linux程序员尽快找到错误代码,从而写出更加专业和优美的代码。先来读读例3-2所示的程序,这段代码写得很糟糕,仔细检查一下不难挑出如下毛病:

    ●       main函数的返回值被声明为void,但实际上应该是int;

    ●       使用了GNU语法扩展,即使用long long来声明64位整数,仍不符合ANSI/ISO C语言标准;

    ●       main函数在终止前没有调用return语句。

    实例3-2  bad.c­­­­­­­­­­­­­­­­­­­­­­­­­­­­

     


    #include <stdio.h>

    void main(void)

    {

      long long int var = 1;

      printf("It is not standard C code!\n");

    }

    下面看看gcc是如何帮助程序员来发现这些错误的。当gcc在编译不符合ANSI/ISO C语言标准的源代码时,如果加上了-pedantic选项,那么使用了扩展语法的地方将产生相应的警告信息:

    [david@DAVID david]$ gcc -pedantic bad.c -o bad

    bad.c: In function 'main':

    bad.c:4: warning: ISO C89 does not support 'long long'

    bad.c:3: warning: return type of 'main' is not 'int'

    需要注意的是,-pedantic编译选项并不能保证被编译程序与ANSI/ISO C标准的完全兼容,它仅仅用来帮助Linux程序员离这个目标越来越近。换句话说,-pedantic选项能够帮助程序员发现一些不符合ANSI/ISO C标准的代码,但不是全部。事实上只有ANSI/ISO C语言标准中要求进行编译器诊断的那些问题才有可能被gcc发现并提出警告。

    除了-pedantic之外,gcc还有一些其他编译选项也能够产生有用的警告信息。这些选项大多以-W开头,其中最有价值的当数-Wall了,使用它能够使gcc产生尽可能多的警告信息。例如:

    [david@DAVID david]$ gcc -Wall bad.c -o bad

    bad.c:3: warning: return type of 'main' is not 'int'

    bad.c: In function 'main':

    bad.c:4: warning: unused variable 'var'

    bad.c:6:2: warning: no newline at end of file

    gcc给出的警告信息虽然从严格意义上说不能算作是错误,但很可能成为错误的栖身之所。一个优秀的Linux程序员应该尽量避免产生警告信息,使自己的代码始终保持简洁、优美和健壮的特性。
        在处理警告方面,另一个常用的编译选项是-Werror,它要求gcc将所有的警告当成错误进行处理,这在使用自动编译工具(如make等)时非常有用。如果编译时带上-Werror选项,那么gcc会在所有产生警告的地方停止编译,迫使程序员对自己的代码进行修改。只有当相应的警告信息消除时,才可能将编译过程继续朝前推进。执行情况如下:

    [david@DAVID david]$ gcc -Werror bad.c -o bad

    cc1: warnings being treated as errors

    bad.c: In function 'main':

    bad.c:3: warning: return type of 'main' is not 'int'

    bad.c:6:2: no newline at end of file

    对Linux程序员来讲,gcc给出的警告信息是很有价值的,它们不仅可以帮助程序员写出更加健壮的程序,而且还是跟踪和调试程序的有力工具。建议在用gcc编译源代码时始终带上-Wall选项,并把它逐渐培养成为一种习惯,这对找出常见的隐式编程错误很有帮助。

    3.4  库  依  赖

    在Linux下使用C语言开发应用程序时,完全不使用第三方函数库的情况是比较少见的,通常来讲都需要借助一个或多个函数库的支持才能够完成相应的功能。从程序员的角度看,函数库实际上就是一些头文件(.h)和库文件(.so或者.a)的集合。虽然Linux下大多数函数都默认将头文件放到/usr/include/目录下,而库文件则放到/usr/lib/目录下,但并不是所有的情况都是这样。正因如此,gcc在编译时必须让编译器知道如何来查找所需要的头文件和库文件。

    gcc采用搜索目录的办法来查找所需要的文件,-I选项可以向gcc的头文件搜索路径中添加新的目录。例如,如果在/home/david/include/目录下有编译时所需要的头文件,为了让gcc能够顺利地找到它们,就可以使用-I选项:

    [david@DAVID david]$ gcc david.c -I /home/david/include -o david

    同样,如果使用了不在标准位置的库文件,那么可以通过-L选项向gcc的库文件搜索路径中添加新的目录。例如,如果在/home/david/lib/目录下有链接时所需要的库文件libdavid.so,为了让gcc能够顺利地找到它,可以使用下面的命令:

    [david@DAVID david]$ gcc david.c -L /home/david/lib –ldavid -o david

    值得详细解释一下的是-l选项,它指示gcc去连接库文件david.so。Linux下的库文件在命名时有一个约定,那就是应该以lib三个字母开头。由于所有的库文件都遵循了同样的规范,因此在用-l选项指定链接的库文件名时可以省去lib三个字母。也就是说gcc在对-l david进行处理时,会自动去链接名为libdavid.so的文件。

    Linux下的库文件分为两大类,分别是动态链接库(通常以.so结尾)和静态链接库(通常以.a结尾),两者的差别仅在于程序执行时所需的代码是在运行时动态加载的,还是在编译时静态加载的。默认情况下,gcc在链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库。如果需要的话可以在编译时加上-static选项,强制使用静态链接库。例如,如果在/home/david/lib/目录下有链接时所需要的库文件libfoo.so和libfoo.a,为了让gcc在链接时只用到静态链接库,可以使用下面的命令:

    [david@DAVID david]$ gcc foo.c -L /home/david/lib -static –ldavid -o

    david

    3.5  gcc代码优化

    代码优化指的是编译器通过分析源代码,找出其中尚未达到最优的部分,然后对其重新进行组合,目的是改善程序的执行性能。gcc提供的代码优化功能非常强大,它通过编译选项-On来控制优化代码的生成,其中n是一个代表优化级别的整数。对于不同版本的gcc来讲,n的取值范围及其对应的优化效果可能并不完全相同,比较典型的范围是从0变化到2或3。

    编译时使用选项-O可以告诉gcc同时减小代码的长度和执行时间,其效果等价于-O1。在这一级别上能够进行的优化类型虽然取决于目标处理器,但一般都会包括线程跳转(Thread Jump)和延迟退栈(Deferred Stack Pops)两种优化。

    选项-O2告诉gcc除了完成所有-O1级别的优化之外,同时还要进行一些额外的调整工作,如处理器指令调度等。

    选项-O3则除了完成所有-O2级别的优化之外,还包括循环展开和其他一些与处理器特性相关的优化工作。

    通常来说,数字越大优化的等级越高,同时也就意味着程序的运行速度越快。许多Linux程序员都喜欢使用-O2选项,因为它在优化长度、编译时间和代码大小之间取得了一个比较理想的平衡点。

    下面通过具体实例来感受一下gcc的代码优化功能,所用程序如例3-3所示。

    实例3-3  count.c­­­­­­­­­­­­­­­­­­­­­­­­­­­­

     


    #include <stdio.h>

     int main(void)

    {  double counter;

       double result;

       double temp;

       for (counter = 0; counter < 4000.0 * 4000.0 * 4000.0  / 20.0 + 2030;   

    counter += (5 - 3 +2 + 1 ) / 4)

         {  temp = counter / 1239;

            result  = counter;   

           } 

       printf("Result is %lf\n", result); 

       return 0;

    }

    首先不加任何优化选项进行编译:

    [david@DAVID david]$ gcc -Wall count.c -o count

    借助Linux提供的time命令,可以大致统计出该程序在运行时所需要的时间:

    [david@DAVID david]$ time ./count

    Result is 3200002029.000000

    real    1m59.357s

    user    1m59.140s

    sys     0m0.050s

    接下来使用优化选项来对代码进行优化处理:

    [david@DAVID david]$ gcc -Wall count.c -o count2

    在同样的条件下再次测试一下运行时间:

    [david@DAVID david]$ time ./count2

    Result is 3200002029.000000

    real    0m26.573s

    user    0m26.540s

    sys     0m0.010s

    对比两次执行的输出结果不难看出,程序的性能的确得到了很大幅度的改善,由原来的1分59秒缩短到了26秒。这个例子是专门针对gcc的优化功能而设计的,因此优化前后程序的执行速度发生了很大的改变。尽管gcc的代码优化功能非常强大,但作为一名优秀的Linux程序员,首先还是要力求能够手工编写出高质量的代码。如果编写的代码简短,并且逻辑性强,编译器就不会做更多的工作,甚至根本用不着优化。

    优化虽然能够给程序带来更好的执行性能,但在如下一些场合中应该避免优化代码。

    ●       程序开发的时候:优化等级越高,消耗在编译上的时间就越长,因此在开发的时候最好不要使用优化选项,只有到软件发行或开发结束的时候,才考虑对最终生成的代码进行优化。

    ●       资源受限的时候:一些优化选项会增加可执行代码的体积,如果程序在运行时能够申请到的内存资源非常紧张(如一些实时嵌入式设备),那就不要对代码进行优化,因为由这带来的负面影响可能会产生非常严重的后果。

    ●       跟踪调试的时候:在对代码进行优化的时候,某些代码可能会被删除或改写,或者为了取得更佳的性能而进行重组,从而使跟踪和调试变得异常困难。

    3.6  加    速

    在将源代码变成可执行文件的过程中,需要经过许多中间步骤,包含预处理、编译、汇编和连接。这些过程实际上是由不同的程序负责完成的。大多数情况下gcc可以为Linux程序员完成所有的后台工作,自动调用相应程序进行处理。

    这样做有一个很明显的缺点,就是gcc在处理每一个源文件时,最终都需要生成好几个临时文件才能完成相应的工作,从而无形中导致处理速度变慢。例如,gcc在处理一个源文件时,可能需要一个临时文件来保存预处理的输出,一个临时文件来保存编译器的输出,一个临时文件来保存汇编器的输出,而读写这些临时文件显然需要耗费一定的时间。当软件项目变得非常庞大的时候,花费在这上面的代价可能会变得很大。

    解决的办法是,使用Linux提供的一种更加高效的通信方式—— 管道。它可以用来同时连接两个程序,其中一个程序的输出将直接作为另一个程序的输入,这样就可以避免使用临时文件,但编译时却需要消耗更多的内存。

    注意:

    在编译过程中使用管道是由gcc的-pipe选项决定的。下面的这条命令就是借助gcc的管道功能来提高编译速度的:

    [david@DAVID david]$ gcc -pipe david.c -o david

    在编译小型工程时使用管道,编译时间上的差异可能还不是很明显,但在源代码非常多的大型工程中,差异将变得非常明显。

    3.7  gcc常用选项

    gcc作为Linux下C/C++重要的编译环境,功能强大,编译选项繁多。为了方便大家日后编译方便,在此将常用的选项及说明罗列出来,见表3-2。

    表3-2  gcc的常用选项

    选  项  名

    作    用

    -c

    通知gcc取消连接步骤,即编译源码并在最后生成目标文件

    -Dmacro

    定义指定的宏,使它能够通过源码中的#ifdef进行检验

    -E

    不经过编译预处理程序的输出而输送至标准输出

    -g3

    获得有关调试程序的详细信息,它不能与-o选项联合使用

    -Idirectory

    在包含文件搜索路径的起点处添加指定目录

    -llibrary

    提示连接程序在创建最终可执行文件时包含指定的库

    -O、-O2、-O3

    将优化状态打开,该选项不能与-g选项联合使用

    -S

    要求编译程序生成来自源代码的汇编程序输出

    -v

    启动所有警报

    .h

    预处理文件(标头文件)

    -Wall

    在发生警报时取消编译操作,即将警报看作是错误

    -w

    禁止所有的报警

     

    3.8  gcc的错误类型及对策

    如果gcc编译器发现源程序中有错误,就无法继续进行,也无法生成最终的可执行文件。为了便于修改,gcc给出错误信息,必须对这些错误信息逐个进行分析、处理,并修改相应的源代码,才能保证源代码的正确编译连接。.gcc给出的错误信息一般可以分为四大类,下面我们分别讨论其产生的原因和对策。

    ●       第一类:C语法错误

    错误信息:文件source.c中第n行有语法错误(syntex errror)。这种类型的错误,一般都是C语言的语法错误,应该仔细检查源代码文件中第n行及该行之前的程序,有时也需要对该文件所包含的头文件进行检查。有些情况下,一个很简单的语法错误,gcc会给出一大堆错误,我们最主要的是要保持清醒的头脑,不要被其吓倒,必要的时候再参考一下C语言的基本教材。在这里推荐一本由Andrew Koenig写的《C 陷阱与缺陷》(此书已由人民邮电出版社翻译出版),说得夸张一点就是此书可以帮助你减少C代码和初级C++代码中的90%的bug。

    ●       第二类:头文件错误

    错误信息:找不到头文件head.h(Can not find include file head.h)。这类错误是源代码文件中包含的头文件有问题,可能的原因有头文件名错误、指定的头文件所在目录名错误等,也可能是错误地使用了双引号和尖括号。

    ●       第三类:档案库错误

    错误信息:连接程序找不到所需的函数库,例如:

    ld: -lm: No such file or directory

    这类错误是与目标文件相连接的函数库有错误,可能的原因是函数库名错误、指定的函数库所在目录名称错误等。检查的方法是使用find命令在可能的目录中寻找相应的函数库名,确定档案库及目录的名称并修改程序中及编译选项中的名称。

    ●       第四类:未定义符号

    错误信息:有未定义的符号(Undefined symbol)。这类错误是在连接过程中出现的,可能有两种原因:一是用户自己定义的函数或者全局变量所在源代码文件,没有被编译、连接,或者干脆还没有定义,这需要用户根据实际情况修改源程序,给出全局变量或者函数的定义体;二是未定义的符号是一个标准的库函数,在源程序中使用了该库函数,而连接过程中还没有给定相应的函数库的名称,或者是该档案库的目录名称有问题,这时需要使用档案库维护命令ar检查我们需要的库函数到底位于哪一个函数库中,确定之后,修改gcc连接选项中的-l和-L项。

    排除编译、连接过程中的错误,应该说只是程序设计中最简单、最基本的一个步骤,可以说只是开了个头。这个过程中的错误,只是我们在使用C语言描述一个算法中所产生的错误,是比较容易排除的。我们写一个程序,到编译、连接通过为止,应该说刚刚开始,程序在运行过程中所出现的问题,是算法设计有问题,说得严重点儿是对问题的认识和理解不够,还需要更加深入地测试、调试和修改。一个程序,稍为复杂的程序,往往要经过多次的编译、连接、测试和修改。 gcc是在Linux下开发程序时必须掌握的工具之一。

    以上对gcc作了一个简要的介绍,主要讲述了如何使用gcc编译程序、产生警告信息、和加快gcc的编译速度。对所有希望早日跨入Linux开发者行列的人来说,gcc就是成为一名优秀的Linux程序员的起跑线。关于调试 C 程序的更多信息请看第4章关于gdb的内容。

    展开全文
  • 详解Linux安装GCC方法

    万次阅读 多人点赞 2018-04-04 09:13:48
    捞nginx的时候回过头来看gcc的安装, 才发现这篇怎么这么长, 还是转载的! 我自己都特么看不下去了! 现重新总结一下, 简单粗暴的两行命令(班门弄斧): 一.安装 yum -y install gcc gcc-c++ autoconf pcre pcre-devel ...

    捞nginx的时候回过头来看gcc的安装, 才发现这篇怎么这么长, 还是转载的! 我自己都特么看不下去了! 现重新总结一下, 简单粗暴的两行命令(班门弄斧):

    一.安装

    yum -y install gcc gcc-c++ autoconf pcre pcre-devel make automake
    yum -y install wget httpd-tools vim

     1.就把gcc当成c语言编译器, g++当成c++语言编译器用就是了.(知乎)

    2.wget是一个从网络上自动下载文件的自由工具, 可以在用户退出系统的之后在继续后台执行, 直到下载任务完成.(百度百科)

     二.测试(查看版本信息, 编译Helloworld)

    a.查看gcc版本信息

    gcc --version

    b.编写Helloworld

    创建名为ctest.c文件

    touch ctest.c

    编辑该文件

    #include <stdio.h>
    
    int main()
    {
    printf("hello world!\n");
    return 0;
    }

     编译gcc ctest.c

    可以看到生成了a.out文件

    执行a.out

    ./a.out

    输出结果

     

     

    =======以下为转载(太tm长,直接忽略不看了)=======

     

    本文转自:http://blog.csdn.net/bulljordan23/article/details/7723495/

    下载: http://ftp.gnu.org/gnu/gcc/gcc-4.5.1/gcc-4.5.1.tar.bz2
    浏览: http://ftp.gnu.org/gnu/gcc/gcc-4.5.1/
    查看Changes: http://gcc.gnu.org/gcc-4.5/changes.htm

    现在很多程序员都应用GCC,怎样才能更好的应用GCC。目前,GCC可以用来编译C/C++、FORTRAN、Java、OBJC、ADA等语言的程序,可根据需要选择安装支持的语言。本文以在Redhat Linux安装GCC4.1.2为例(因在项目开发过程中要求使用,没有用最新的GCC版本),介绍Linux安装GCC过程。

    安装之前,系统中必须要有cc或者gcc等编译器,并且是可用的,或者用环境变量CC指定系统上的编译器。如果系统上没有编译器,不能安装源代码形式的GCC 4.1.2。如果是这种情况,可以在网上找一个与你系统相适应的如RPM等二进制形式的GCC软件包来安装使用。本文介绍的是以源代码形式提供的GCC软件包的安装过程,软件包本身和其安装过程同样适用于其它Linux和Unix系统。

    系统上原来的GCC编译器可能是把gcc等命令文件、库文件、头文件等分别存放到系统中的不同目录下的。与此不同,现在GCC建议我们将一个版本的GCC安装在一个单独的目录下。这样做的好处是将来不需要它的时候可以方便地删除整个目录即可(因为GCC没有uninstall功能);缺点是在安装完成后要做一些设置工作才能使编译器工作正常。在本文中采用这个方案安装GCC 4.1.2,并且在安装完成后,仍然能够使用原来低版本的GCC编译器,即一个系统上可以同时存在并使用多个版本的GCC编译器。

    按照本文提供的步骤和设置选项,即使以前没有安装过GCC,也可以在系统上安装上一个可工作的新版本的GCC编译器。

    1 下载

    在GCC网站上(http://gcc.gnu.org)或者通过网上搜索可以查找到下载资源。目前GCC的最新版本为 4.2.1。可供下载的文件一般有两种形式:gcc-4.1.2.tar.gz和gcc-4.1.2.tar.bz2,只是压缩格式不一样,内容完全一致,下载其中一种即可。

    2. 解压缩

    拷贝gcc-4.1.2.tar.bz2(我下载的压缩文件)到/usr/local/src(根据自己喜好选择)下,根据压缩格式,选择下面相应的一种方式解包(以下的“%”表示命令行提示符):

    % tar zxvf gcc-4.1.2.tar.gz

    或者

    % bzcat gcc-4.1.2.tar.bz2 | tar xvf -

    新生成的gcc-4.1.2这个目录被称为源目录,用${srcdir}表示它。以后在出现${srcdir}的地方,应该用真实的路径来替换它。用pwd命令可以查看当前路径。

    在${srcdir}/INSTALL目录下有详细的GCC安装说明,可用浏览器打开index.html阅读。

    3. 建立目标目录

    目标目录(用${objdir}表示)是用来存放编译结果的地方。GCC建议编译后的文件不要放在源目录${srcdir]中(虽然这样做也可以),最好单独存放在另外一个目录中,而且不能是${srcdir}的子目录。

    例如,可以这样建立一个叫 /usr/local/gcc-4.1.2的目标目录:

    % mkdir /usr/local/gcc-4.1.2

    % cd gcc-4.1.2

    以下的操作主要是在目标目录 ${objdir} 下进行。(否则会出错,后面有解释)

    4. 配置

    配置的目的是决定将GCC编译器安装到什么地方(${destdir}),支持什么语言以及指定其它一些选项等。其中,${destdir}不能与${objdir}或${srcdir}目录相同。

    配置是通过执行${srcdir}下的configure来完成的。其命令格式为(记得用你的真实路径替换${destdir}):

    % ${srcdir}/configure --prefix=${destdir} [其它选项]

    例如,如果想将GCC 4.1.2安装到/usr/local/gcc-4.1.2目录下,则${destdir}就表示这个路径。

    在我的机器上,我是这样配置的:

    % ../gcc-4.1.2/configure --prefix=/usr/local/gcc-4.1.2 --enable-threads=posix --disable-checking --enable--long-long --host=i386-redhat-linux--with-system-zlib --enable-languages=c,c++,java

    将GCC安装在/usr/local/gcc-4.1.2目录下,支持C/C++和JAVA语言,其它选项参见GCC提供的帮助说明。

    5. 编译

    % make

    6. 安装

    执行下面的命令将编译好的库文件等拷贝到${destdir}目录中(根据你设定的路径,可能需要管理员的权限):

    % make install

    至此,GCC 4.1.2安装过程就完成了。

    7. 其它设置

    GCC 4.1.2的所有文件,包括命令文件(如gcc、g++)、库文件等都在${destdir}目录下分别存放,如命令文件放在bin目录下、库文件在 lib下、头文件在include下等。由于命令文件和库文件所在的目录还没有包含在相应的搜索路径内,所以必须要作适当的设置之后编译器才能顺利地找到并使用它们。

    7.1 gcc、g++、gcj的设置

    要想使用GCC 4.1.2的gcc等命令,简单的方法就是把它的路径${destdir}/bin放在环境变量PATH中。我不用这种方式,而是用符号连接的方式实现,这样做的好处是我仍然可以使用系统上原来的旧版本的GCC编译器。

    首先,查看原来的gcc所在的路径:

    % which gcc

    在我的系统上,上述命令显示:/usr/bin/gcc。因此,原来的gcc命令在/usr/bin目录下。我们可以把GCC 4.1.2中的gcc、g++、gcj等命令在/usr/bin目录下分别做一个符号连接:

    % cd /usr/bin

    % ln -s ${destdir}/bin/gcc gcc412

    % ln -s ${destdir}/bin/g++ g++412

    % ln -s ${destdir}/bin/gcj gcj412

    这样,就可以分别使用gcc412、g++412、gcj412来调用GCC 4.1.2的gcc、g++、gcj完成对C、C++、JAVA程序的编译了。同时,仍然能够使用旧版本的GCC编译器中的gcc、g++等命令。

    (cool,我感觉棒极了!!1)

    7.2 库路径的设置

    将${destdir}/lib路径添加到环境变量LD_LIBRARY_PATH中,例如,如果GCC 4.1.2安装在/usr/local/gcc-4.1.2目录下,在RH Linux下可以直接在命令行上执行

    % export LD_LIBRARY_PATH=/usr/local/gcc-4.1.2/lib

    最好添加到系统的配置文件中,这样就不必要每次都设置这个环境变量了,在文件$HOME/.bash_profile中添加下面两句:

    LD_LIBRARY_PATH=/usr/local/gcc-4.1.2/lib:$LD_LIBRARY_PATH

    export LD_LIBRARY_PATH

    重启系统设置生效,或者执行命令

    % source $HOME/.bash_profile

    用新的编译命令(gcc412、g++412等)编译你以前的C、C++程序,检验新安装的GCC编译器是否能正常工作。

    完成了Linux安装GCC,之后你就能轻松地编辑了。

    from:os.51cto.com/art/200912/168804.htm

    在RHLinux下安装gcc-4.0.1方法比较简单,但是安装过程中有些环节是需要注意的,否则,可能会导致安装不成功,或者安装报错。具体安装过程如下:

    首先,下载并解压缩gcc的RPM包至源目录(如/opt/gcc-4.0.1)

    1、解压缩RPM包:

    [root@linuxopt]# tar xjvf gcc-4.0.1.tar.bz2 (解压后生成源目录/opt/gcc-4.0.1)

    2、创建安装目标目录:

    [root@linux opt]# mkdir /usr/local/gcc-4.0.1/

    3、进入安装目标目录:

    [root@linux opt]# cd /usr/local/gcc-4.0.1/ (这一步很重要,配置安装文件时,需要在目标目录下执行configure命令)

    [root@linux opt]# pwd

    /usr/local/gcc-4.0.1

    4、配置安装文件:

    [root@linux gcc-4.0.1]# /opt/gcc-4.0.1/configure --prefix=/usr/local/gcc-4.0.1/ (这一步非常重要,需要在安装的目标目录下,执行源目录 /opt/gcc-4.0.1/中的configure命令,配置将gcc安装到目标目录/usr/local/gcc-4.0.1/)

    creating cache ./config.cache

    checking host system type... i686-pc-linux-gnu

    5、编译安装文件:

    [root@linux gcc-4.0.1]# pwd

    /usr/local/gcc-4.0.1

    [root@linux gcc-4.0.1]# make (在目标目录下执行编译)

    6、安装gcc:

    [root@linux gcc-4.0.1]# pwd

    /usr/local/gcc-4.0.1

    [root@linux gcc-4.0.1]# make install (在目标目录下执行安装)

    如果安装过程中步骤和命令没有错误,你肯定能安装成功。

     

    ---------------------------------------------------------------------

     

      首先,现在最新的包GCC 4.2.bz2,一般的到处都有的下,linux下的下载速度很满,20多k,很恶心,我喜欢迅雷的下载速度,在windows下下载,大概42M,下载速度2到4M,回到linux下,挂载,ntfs格式的我的,具体不说了,说安装! 

      进入到挂载的目录下,先cp GCC4.2.bz2 /azuo, 

      cd /azuo, 

      tar -xvf GCC 4.2.bz2 , 

      得到gcc-4.2, 

      cd gcc-4.2,在/usr目录下建立一个文件夹就是现在要存放新的gcc的目录, 

      mkdir /usr/gcc4 

      ./configure –prefix=/usr/gcc4 

      回车,就会有配置信息,只要不报错就可以了, 

      make,这个过程很久,因为我没有设置一些具体的选项,因此,所有的组件几乎都要编译一遍,我的电脑cpu:AMD 3200+X2 ,1.5g内存,大概花费一个半小时。 

      到这个时候为止,/usr/gcc4下还没有任何东西,编译过程都是发生在源文件夹,让我们再来一个动作,所有的要用到的东西都会配置到目标文件夹下, 

      make install; 

      这个过程也不短,等着就是了。 

      一切都已经弄好了之后就是使用最新的gcc了,可以看到,在/usr/gcc4/bin下有gcc,g++,等一些东西,都是可以用的,写两个程序: 

      aa.c: 

      1 #include 

      2 

      3 int main(void) { 

      4 printf("ad"); 

      5 return 1; 

      6 } 

      gcc -o aa aa.c 

      执行 ./aa 

      上面的这个是c的,下面这个就是c++的了: 

      a.cpp: 

      1 #include 

      2 using namespace std; 

      3 int main(void) { 

      4 cout << "Ok!" << endl; 

      5 return 1; 

      6 } 

      g++ -o a a.cpp 

      执行就是了./a 

      一切ok了,就可以让最新的gcc工具取代原来的工具了。 

      看看原来的gcc是什么版本的,我们好卸载它: 

      [root@BTazuo bin]# rpm -qa gcc 

      gcc-4.1.2-27.fc7 

      [root@BTazuo bin]# rpm -e gcc-4.1.2-27.fc7 

      error: Failed dependencies: 

      gcc is needed by (installed) systemtap-0.5.13-1.fc7.i386 

      gcc = 4.1.2-27.fc7 is needed by (installed) gcc-c++-4.1.2-27.fc7.i386 

      gcc = 4.1.2-27.fc7 is needed by (installed) gcc-gfortran- 4.1.2 -27.fc7. i386 

      [root@BTazuo bin]# rpm -e gcc-c++-4.1.2-27.fc7.i386 

      [root@BTazuo bin]# rpm -e gcc-gfortran-4.1.2-27.fc7.i386 

      [root@BTazuo bin]# rpm -e gcc-4.1.2-27.fc7 

      error: Failed dependencies: 

      gcc is needed by (installed) systemtap-0.5.13-1.fc7.i386 

      [root@BTazuo bin]# g++ 

      bash: g++: command not found 

      卸载成功 

      [root@BTazuo bin]# gcc 

      gcc: 没有输入文件 ,可见gcc犹在 

      [root@BTazuo bin]# rpm -e systemtap-0.5.13-1.fc7.i386 

      [root@BTazuo bin]# gcc 

      gcc: 没有输入文件 

      [root@BTazuo bin]# rpm -e gcc-4.1.2-27.fc7 

      [root@BTazuo bin]# gcc 

      bash: /usr/lib/ccache/gcc: 没有那个文件或目录 

      最后的卸载成功! 

      这个时候,要注意了,我的gcc在/usr/bin下面有,在/usr/lib/ccache这个目录下也有,分别在这两个下面都要建立一个链接: 

      [root@BTazuo bin]# ln -s /usr/gcc4/bin/g++ g++ 

      [root@BTazuo bin]# g++ 

      g++: 没有输入文件 

      可见g++已经装好了,可以使用了。 

      然后是gcc: 

      [root@BTazuo bin]# ln -s /usr/gcc4/bin/gcc gcc 

      [root@BTazuo bin]# gcc 

      bash: /usr/lib/ccache/gcc: 没有那个文件或目录 

      [root@BTazuo bin]# ./gcc 

      gcc: 没有输入文件 

      可见还要给另外一个目录建立一个gcc的链接: 

      [root@BTazuo lib]# ln -s /usr/gcc4/bin/gcc /usr/lib/ccache/gcc 

      [root@BTazuo lib]# gcc 

      gcc: 没有输入文件 

      到此为止,gcc和g++都已经建立好了,可以用了,最后把原来的包和解压文件都可以删除了,以节省硬盘空间!

    -------------------------------------------------------------------------------------------------------

    下面介绍其在Red Hat Linux 9.0编译器给gcc 3.2.2环境下的安装方法。
    一.确定安装环境
    本安装方法适用于Red Hat Linux 9.0操作系统,自带的GCC编译器是GCC 3.2.2版本。其他系列的linux操作系统或是其他版本GCC下安装过程可能有些细节上的不同。
    注:因为不同版本的GCC编译器下一些库的定义可能有不同,因此首先要确定一下本机的GCC编译器版本。确定方法是进入命令行输入命令gcc –v得到的结果如图1.1所示:


    图1.1
    可见本机的GCC版本为GCC 3.2.2,根据simplescalar网站上的一些介绍,该模拟器的开发工具可能是GCC2.7左右,比较接近GCC 3.2.2,因此估计安装过程会比较顺利。
    二.获得安装包
    完成本安装过程的安装包可以在

    http://www.simplescalar.com
    下下载,本安装所需要的安装包共有以下三个

    三 建立安装目录,解压安装包
    建立安装目录为/root/simplescalar,将安装包复制到安装目录,整个过程如下:

    解压缩,命令为tar –zxvf,加压缩完毕后用rm*.tgz命令删除压缩包,整个过程输入命令如下:
    tar –zxvf simplesim-3v0d.tgz
    tar –zxvf simpletools-2v0.tgz
    tar –zxvf simpleutils-2v0.tgz
    rm*.tgz
    加压缩后得到如下七个文件夹:

    四.安装binutils2.5.2
    首先用configure命令配置程序的安装环境和参数,生成Makefile文件,整个过程如下:

    注:configure命令的参数含义说明
    -host:配置安装环境
    -target:配置成littleEndian模式
    -with-gnu-as 加载汇编器
    -with-gnu-ld 加载链接器
    -prefix 设置安装目录
    此时遇到两个错误,如下所示:
      

    从错误说明可以估计出错误来自于libiberty文件夹下的dummy.c文件。网上一些帖子说是dummy.c中定义的宏functions.def文件中的函数定义与声明不一致引起的,将它们改成一致就可以通过编译。但是这样做的话,继续编译依然会遇到许多错误。这里我试出了最好的方法就是将dummy.c文件中的内容全部删除(即将dummy.c变成空文件)然后再make一次,这回不报任何错误,编译一次通过!
    运行make install命令,这时binutils-2.5.2安装成功!
    五.安装simplescalar
    Simplescalar是最简单的一个安装过程,运行下列命令即可完成安装!


    六.安装gcc-2.6.3
    安装好simplescalar后在安装文件夹/root/simplescalar下可以找到一个名为bin的文件夹,里面包含的是一些simplescalar自带的工具,如链接工具等,该文件内容如下:


    可见,里面没有C编译工具gcc,因此还需要安装gcc2.6.3作为simplescalar的内置编译工具,下面介绍安装方法。
    首先对安装环境进行配置,生成Makefile文件,具体方法如下图所示。


    Makefile文件生成完毕后,运行make命令,此时出现以下错误:


    由错误报告可知,这是由于sys_errlist的定义不一致造成的,打开cccp.c文件发现其194行附近有如下代码,第194行为extern char *sys_errlist[]。显然要消除冲突,只需改变宏编译的分支方向,使其不走这一分支即可。尝试在这段代码前面如175行加上#define bsd4_4,修改后再次make,该错误改正,遇到下一个错误。
      

    第二个错误是sdbout.c文件中的一些常量没有定义,如下:



    因此第一估计是头文件的问题,打开sdbout.c发现其包含的头文件有如下几个: 

    可以肯定报错的原因是宏编译的分支的问题(走不同的宏编译分支,可能包含syms.h或者是gsyms.h)。经过多次尝试,发现在前面加上#undef  USG即可解决该错误(即把gsyms.h包含进去)。
    继续make,发生第三个错误,如下:


    这个错误和第一个错误一样,是由于sys_errlist定义冲突引起的,因此进入gcc.c文件,在172行之前(这里加在167行)加上#define bsd4_4即可。
    继续make,发生第四个错误,如下:


    和前面一样,只要在g++.c文件的第90行代码段之前(这里加在85行)加上#define bsd4_4即可。
    继续make,发生第五个错误,如下: 


    这里提示是将cp/g++.c文件中第213行的sys_errlist改成strerror或者strerror_r,然而改过之后会报strerror未定义的错误,上网搜了很久也没发现有这两个定义的头文件名,最后根据函数名感觉这段代码(pfatal_with_name)的功能可能是获得错误名,将其删除估计对工作影响不大,因此干脆将这个函数放空,放空后再运行make,果然OK,不报任何错误,编译通过!
    运行make install,gcc 2.6.3安装成功!
    返回simplescalar,进入bin文件夹,可以发现里面多了一个sslittle-na-sstrix-gcc文件,该文件是simplescalar的内建C编译器,如下所示:


    进一步运行./sslittle-na-sstrix-gcc –v测试出该内建编译器版本为gcc 2.6.3。


    到此为止,整个安装过程结束,下面进行测试。
    七.测试
    为了测试simplescalar是否能够顺利运行,我们对其进行测试,测试程序依然采用最经典的hello world!程序,程序如下:
        #include 
    main()
    {
    printf("Hello World!\n");
    return 0;
    }
    编辑好程序后将其保存在/root/simplescalar文件夹下,文件名为hello.c,用刚刚安装的simplescalar内建编译器编译,编译方法如下。


        运行结果如下:


    八 结束语
    本安装方法适用于Red Hat Linux 9.0操作系统,自带的GCC编译器是GCC 3.2.2版本。其他系列的linux操作系统或是其他版本GCC下安装过程可能有些细节上的不同,主要原因可能是高版本的GCC没有兼容低版本的一些库文件,还有就是遵循的C标准可能会有点出入。如果机器上的GCC正好是低版本的,估计可能一次编译通过!
    -----------------------------------------------------------------------------------------------------

      1. 下载 
      在GCC网站上(
    http://gcc.gnu.org/
    )或通过网上搜索能查找到下载资源。目前GC
    C的最新版本为3.4.0。可供下载的文件一般有两种形式:gcc-3.4.0.tar.gz和gcc-3.4.0.tar.bz2,
    只是压缩格式不相同,内容完全一致,下载其中一种即可。 
      2. 解压缩 
      根据压缩格式,选择下面相应的一种方式解包(以下的"%"表示命令行提示符): 
      % tar xzvf gcc-3.4.0.tar.gz

    % bzcat gcc-3.4.0.tar.bz2 | tar xvf - 
      新生成的gcc-3.4.0这个目录被称为源目录,用${srcdir}表示他。以后在出现${srcdir
    }的地方,应该用真实的路径来替换他。用pwd命令能查看当前路径。 
      在${srcdir}/INSTALL目录下有周详的GCC安装说明,可用浏览器打开index.html阅读。 
      3. 建立目标目录 
      目标目录(用${objdir}表示)是用来存放编译结果的地方。GCC建议编译后的文件不要
    放在源目录${srcdir]中(虽然这样做也能),最佳独立存放在另外一个目录中,而且不
    能是${srcdir}的子目录。 
      例如,能这样建立一个叫 gcc-build
    的目标目录(和源目录${srcdir}是同级目录): 
      % mkdir gcc-build
    % cd gcc-build 
      以下的操作主要是在目标目录 ${objdir} 下进行。 
      4. 设置 
      设置的目的是决定将GCC编译器安装到什么地方(${destdir}),支持什么语言及指
    定其他一些选项等。其中,${destdir}不能和${objdir}或${srcdir}目录相同。 
      设置是通过执行${srcdir}下的configure来完成的。其命令格式为(记得用你的真实路
    径替换${destdir}): 
      % ${srcdir}/configure --prefix=${destdir} [其他选项] 
      例如,如果想将GCC
    3.4.0安装到/usr/local/gcc-3.4.0目录下,则${destdir}就表示这个路径。 
      在我的机器上,我是这样设置的: 
      % ../gcc-3.4.0/configure --prefix=/usr/local/gcc-3.4.0
    --enable-threads=posix --disable-checking --enable--long-long
    --host=i386-redhat-linux --with-system-zlib --enable-languages=c,c++,java 
      将GCC安装在/usr/local/gcc-3.4.0目录下,支持C/C++和JAVA语言,其他选项参见GCC
    提供的帮助说明。 
      5. 编译 
      % make 
      这是个漫长的过程。在我的机器上(P4-1.6),这个过程用了50多分钟。 
      6. 安装 
      执行下面的命令将编译好的库文件等拷贝到${destdir}目录中(根据你设定的路径,可
    能需要管理员的权限): 
      % make install 
      至此,GCC 3.4.0安装过程就完成了。 
      6. 其他设置 
      GCC
    3.4.0的所有文件,包括命令文件(如gcc、g++)、库文件等都在${destdir}目录下分别存
    放,如命令文件放在bin目录下、库文件在lib下、头文件在include下等。由于命令文件和
    库文件所在的目录还没有包含在相应的搜索路径内,所以必须要作适当的设置之后编译器才
    能顺利地找到并使用他们。 
      6.1 gcc、g++、gcj的设置 
      要想使用GCC
    3.4.0的gcc等命令,简单的方法就是把他的路径${destdir}/bin放在环境变量PATH中。我不
    用这种方式,而是用符号连接的方式实现,这样做的好处是我仍然能使用系统上原来的旧
    版本的GCC编译器。 
      首先,查看原来的gcc所在的路径: 
      % which gcc 
      在我的系统上,上述命令显示:/usr/bin/gcc。因此,原来的gcc命令在/usr/bin目录
    下。我们能把GCC
    3.4.0中的gcc、g++、gcj等命令在/usr/bin目录下分别做一个符号连接: 
      % cd /usr/bin
    % ln -s ${destdir}/bin/gcc gcc34
    % ln -s ${destdir}/bin/g++ g++34
    % ln -s ${destdir}/bin/gcj gcj34 
      这样,就能分别使用gcc34、g++34、gcj34来调用GCC
    3.4.0的gcc、g++、gcj完成对C、C++、JAVA程式的编译了。同时,仍然能够使用旧版本的GC
    C编译器中的gcc、g++等命令。 
      6.2 库路径的设置 
      将${destdir}/lib路径添加到环境变量LD_LIBRARY_PATH中,最佳添加到系统的设置文
    件中,这样就不必要每次都设置这个环境变量了。 
      例如,如果GCC 3.4.0安装在/usr/local/gcc-3.4.0目录下,在RH
    Linux下能直接在命令行上执行或在文件/etc/profile中添加下面一句: 
      setenv LD_LIBRARY_PATH /usr/local/gcc-3.4.0/lib:$LD_LIBRARY_PATH 
      7. 测试 
      用新的编译命令(gcc34、g++34等)编译你以前的C、C++程式,检验新安装的GCC编译
    器是否能正常工作。 
      8. 根据需要,能删除或保留${srcdir}和${objdir}目录。 

    展开全文
  • Gcc简介

    千次阅读 2018-11-04 10:46:42
    一、什么是Gcc Linux系统下的Gcc(GNU C Compiler)是GNU推出的功能强大、性能优越的多平台编译器,是GNU的代表作品之一。gcc是可以在多种硬体平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比...

    一、什么是Gcc

    Linux系统下的Gcc(GNU C Compiler)是GNU推出的功能强大、性能优越的多平台编译器,是GNU的代表作品之一。gcc是可以在多种硬体平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比平均效率要高20%~30%。

    Gcc编译器能将C、C++语言源程序、汇程式化序和目标程序编译、连接成可执行文件,如果没有给出可执行文件的名字,gcc将生成一个名为a.out的文件。在Linux系统中,可执行文件没有统一的后缀,系统从文件的属性来区分可执行文件和不可执行文件。

    二、gcc所遵循的部分约定规则

    前面提到编译的后缀问题,而gcc则通过后缀来区别输入文件的类别,下面我们来介绍gcc所遵循的部分约定规则。

    .c为后缀的文件,C语言源代码文件;

    .a为后缀的文件,是由目标文件构成的档案库文件;
    .C或.cc或.cxx为后缀的文件,是C++源代码文件;

    .h为后缀的文件,是程序所包含的头文件;

    .i为后缀的文件,是已经预处理过的C源代码文件;

    .ii为后缀的文件,是已经预处理过的C++源代码文件;

    .m为后缀的文件,是Objective-C源代码文件;

    .o为后缀的文件,是编译后的目标文件;

    .s为后缀的文件,是汇编语言源代码文件;

    .S为后缀的文件,是经过预编译的汇编语言源代码文件。

    三、Gcc的执行过程

    虽然我们称Gcc是C语言的编译器,但使用gcc由C语言源代码文件生成可执行文件的过程不仅仅是编译的过程,而是要经历四个相互关联的步骤∶预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和连接(Linking)。

    ●命令gcc首先调用cpp进行预处理,在预处理过程中,对源代码文件中的文件包含(include)、预编译语句(如宏定义define等)进行分析。

    ●接着调用cc1进行编译,这个阶段根据输入文件生成以.o为后缀的目标文件。汇编过程是针对汇编语言的步骤,调用as进行工作,一般来讲,.S为后缀的汇编语言源代码文件和汇编、.s为后缀的汇编语言文件经过预编译和汇编之后都生成以.o为后缀的目标文件。

    ●当所有的目标文件都生成之后,gcc就调用ld来完成最后的关键性工作,这个阶段就是连接。在连接阶段,所有的目标文件被安排在可执行程序中的恰当的位置,同时,该程序所调用到的库函数也从各自所在的档案库中连到合适的地方。

    四、Gcc的基本用法和选项

    在使用Gcc编译器的时候,我们必须给出一系列必要的调用参数和文件名称。Gcc编译器的调用参数大约有100多个,其中多数参数我们可能根本就用不到,这里只介绍其中最基本、最常用的参数

    Gcc最基本的用法是∶gcc[options] [filenames] ,其中options就是编译器所需要的参数,filenames给出相关的文件名称。

    五、Gcc的参数选项

    -c,只编译,不连接成为可执行文件,编译器只是由输入的.c等源代码文件生成.o为后缀的目标文件,通常用于编译不包含主程序的子程序文件。

    -o output_filename,确定输出文件的名称为output_filename,同时这个名称不能和源文件同名。如果不给出这个选项,gcc就给出预设的可执行文件a.out。

    -g,产生符号调试工具(GNU的gdb)所必要的符号资讯,要想对源代码进行调试,我们就必须加入这个选项。

    -O,对程序进行优化编译、连接,采用这个选项,整个源代码会在编译、连接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是,编译、连接的速度就相应地要慢一些。

    -O2,比-O更好的优化编译、连接,当然整个编译、连接过程会更慢。
    -Idirname,将dirname所指出的目录加入到程序头文件目录列表中,是在预编译过程中使用的参数。

    展开全文
  • Linux编程之GCC编译工具实战

    千人学习 2019-06-24 13:31:17
    本课程使得学员能够学会在linux中如何编译C/C++程序,使用GCC工具如何编译静态库,动态库,如何配置运行动态库。
  • gcc 4.8.x+安装(gcc4.8 c++11 glibc)

    千次阅读 2018-09-20 13:59:17
    交叉工具链制作至尊宝典 https://blog.csdn.net/turui/article/details/6596093 /lib64/libc.so.6: version `GLIBC_2.14’ not found问题 ...GCC升级 支持C++11 https://blog.csdn.net/oa...
  • [CentOS]如何解决gcc版本冲突?

    千次阅读 2014-07-01 18:40:03
    yum -y install make gcc gcc-c++ kernel-devel m4 ncurses-devel openssl-devel 结果导致编译cocos2d-x出现错误: cc1plus: error: unrecognized command line option "-std=c++11
  • gcc升级到4.8.2

    2019-01-22 11:22:03
    近日在一台CentOS6.5的机器上编译程序,发现无法识别“gnu c++ 11”语法,检查当前CentOS6.5的gcc版本为:4.4.7    [root@123 bin]# gcc -v  Using built-in specs.  Target: x86_64-redhat-linux  Thread ...
  • VS版本、GCC版本与C++版本的对应关系

    万次阅读 2019-09-10 20:02:33
    做嵌入式开发经常遇到一些编译器和C++版本不匹配的问题,现整理如下: 目前C语言的标准有:C89(ANSI C)、C90、C95、C99(ISO C)、C11(C1x) 目前C++语言的标准有:C++98、C++03(对98小幅修改)、C++11(全面进化...
  • gcc&gcc;-c++4.8version

    2020-07-30 23:31:51
    gccgcc-c++4.8版本所需要的rpm包,亲测可用,具体使用可以查看本人博客
  • gcc

    千次阅读 2019-10-18 19:46:40
    gcc:一个工具集合,包含预处理器,编辑器,汇编器,链接器等组件 说明:当不使用任何选项时,gcc将会生成一个名为a.out的可执行文件 gcc选项 gcc -E 预处理 .i gcc -S 编译成汇编代码 .s gcc -c 汇编成目标代码 .o ...
  • GCC强大背后

    千次阅读 2015-01-23 13:04:07
    前记: 经常浏览博客园...GCC,全称GNU Compiler Collection,是一套GNU开发的编译器环境,它的创始人便是大名鼎鼎的Richard.M.Stallman。最初GCC刚开始开发时,它还叫做GNU C Compiler,随着开发的深入,GCC很快得到
  • gcc官网下载地址

    万次阅读 2013-03-13 15:02:55
    gcc官网: http://gcc.gnu.org/ 各版本下载地址 ftp://ftp.mirrorservice.org/sites/sourceware.org/pub/gcc/releases/gcc-4.7.2/ infrastructure目录下寻找下载【必须】的mpc-0.8.1.tar.gz、mpfr-2.4.2.tar.bz2...
  • [Running] cd "f:\新建文件夹\" && gcc sa.c -o sa && "f:\新建文件夹\"sa 'gcc' �����ڲ����ⲿ���Ҳ���ǿ����еij��� ���������ļ��� [Done] exited with code=1 in 0....
  • Centos下更新 gcc 版本至 GCC 6.1.0

    万次阅读 2018-06-13 10:58:26
    最近linux的gcc版本更新到gcc 6.1.0 版本了,我查看了一下我的机器上的GCC版本....  ------------- GCC 4.4.7 我去,这也太low了吧,而且我们知道的GCC 4.4.7 是不支持 C++11 的,我的脾气比较暴躁,果断要升级到...
  • 更改gcc默认版本(gcc版本降级/升级)

    万次阅读 2018-09-04 17:35:39
    更改gcc默认版本(gcc版本降级/升级) Ubuntu系统升级到16.04以后,gcc的版本变为6.2,编译比较老的项目编译不过,需要将gcc版本降级到ubunt14.04时候4.8版本. 可以让系统存在两个gcc版本,4.8和6.2,但是默认的gcc版本为...
  • gcc编译基本用法

    万次阅读 2016-05-03 08:11:36
    gcc的基本用法 命令格式:gcc [选项] [文件名] 编译的四个阶段: -E:仅执行编译预处理; -c:仅执行编译操作,不进行连接操作; -S:将C代码转换为汇编代码; -o:指定生成的输出文件。 –c...
  • linux(CentOS7)下安装gcc

    万次阅读 2018-08-18 15:31:16
    1、linux中没有默认安装的gcc,通过yum发现没有可用软件包gcc 原因:这个系统没有注册到Red Hat订阅管理。你可以使用订阅管理器来注册,说白了就是要收费,不给钱不让用。。。。 2、查看系统版本: 百度red ...
  • centos7升级gcc8.2

    万次阅读 热门讨论 2020-09-07 16:06:14
    centos7系统自带的是gcc 4.8.5的,对c++11支持还算比较好。目前,c++14、c++17的标准都已出台,c++20还会远吗? 为了更好的学习和工作,说白了就是为了尝鲜,老广干什么事情都是要尝鲜的,所以身在广东的我,也准备...
  • ubuntu18.04安装gcc详细步骤(附问题集)

    万次阅读 多人点赞 2019-05-22 22:01:16
    首先是下载gcc包,可以在GCC的官方网站http://gcc.gnu.org/下载到各个版本。 目前最高版本是gcc-8.2.0。 一、在安装gcc前,需要先安装 MPFR 、GMP 和MPC GCC编译需要mpfr和mpc(-->gmp、-->mpfr)库的支持,...
1 2 3 4 5 ... 20
收藏数 519,073
精华内容 207,629
关键字:

gcc