查看文章 |
GCC參數篇(1) 作者:陳信宏
2009/03/20 下午 7:29
GNU C/C++是由自由软体协会(FSF;Free Software Foundation)所发展和推出的一套功能 完备的 C/C++语言编译器和程式库,C/C++ 语言 以其类似高阶语言的语法和接进机械语言的执行 速度,成为软体发展的重要选择之一; C/C++语 言的可携带性能力本来就很高,这使得发展完成 的程式只需修改小部份或是根本不需修改,就能 轻易的移植到不同的作业系统上;虽然 C/C++本 身有这些先天上的优点,但更重要的是需要有个 良好的编译程式,才能将可执行档编得更快、编 得更小,而且若是在不同的作业系统上都有一套 相同的编译器,那对于程式设计人员有着莫大的 帮助和方便,因为不会有在那个OS下要用那个编 译程式的困扰;多年来,GNU C/C++ 一直尽职的 扮演这个角色。 如同 Linux一般,GNU CC非由一人或一公司 所撰写,除了Richard Stallman先生外,全世界 的使用者也供献了许多心力,有人加入新的关念 和技巧,有人改善旧有的结构,更有人把她移植 到不同的机型和作业系统上,在夜以继日时时刻 刻的改进之下,GNU CC汇集了各方的优点加以融 合,并朝未来进步之中;在 Linux中就是以她作 为发展工具,在重编核心之时便是她担任重责大 任,而且在 DOS中也有GNU CC-- DJGPP。然而她 并未提供如 Turbo C的整合发展环境,想要认识 她的使用者难免会有吃力的感觉,面对如此的仰 之弥高,最直接的方法便是从控制功能的参数认 识起,如此一来不但不会鑽之弥坚,而且还能渐 入佳境,因此我们就先从参数谈起。 GNU C/C++ 的一些相关资料 ======================== gcc 是 GNU C/C++的 C语言编译程式的名称 ,在接下来的文章之中,笔者将以大写的 GCC来 代表 GNU C/C++,而以小写的 gcc来代表编译程 式,gcc 的语法如下: gcc [option|filemane]... 其中filemane是将要编译的档名,option是将要 认识的参数部份。 当要编译 C++语言时,g++ 为使用的编译程 式,在此 g++是一个完全的编译器--而不只是个 前置处理器,直接产生物件码,这样的 C++执行 起来才更有效率,其语法如下: g++ [option|filemane]... 其中参数部份除非有指明是专属于 gcc或是 g++ 外,其馀两者皆可使用。 在一般的情况下,GCC 的编译过程有下列四 个步骤:preprocessing,compilation,assembly 和 linking,而整体参数(overall options) 的 作用是指定在那一阶段停止,其它类型的参数主 要是调整这四个步骤中某一步骤的动作。 由于许多参数之后都允许加上其它控制的子 参数,因此对于参数间的合併使用有较严格的限 定--两个参数不可併成一个,如 -dr这个参数便 和 -d -r在作用上有着极大的不同。对于不同类 型的参数,在排列上不需依任何顺序。而相同类 型的参数,也没有只能使用一个的限制。对于某 些参数如-f或是-W,其后的子参数可能是一个字 串,如 -fforce-mem或是-Wformat,这类型的组 合大部份都有posetive和negative两种型态,如 -ffoo的negative型为-fno-foo,在接下来的讨 论之中,若是这两种型态皆存在者,笔者会一同 列出。 整体参数(Overall Options) ========================= 整体参数的目的在于控制编译的进行,即编 译到那个阶段便停止,因此我们先来看看各种字 尾和档桉间的关係: 'FILE.c' 需作前置处理(preprocess)的 C语言 原始程式 'FILE.i' 不需作前置处理的 C语言原始程式 'FILE.m' Objective-C 的原始程式,且必需和 程式库 'libobjc.a'连结才能植执行 'FILE.h' C 语言标头档(header file) 'FILE.cc' 'FILE.cxx' 'FILE.C' 需作前置处理的 C++语言原始程式 'FILE.ii' 不需作前置处理的 C++语言原始程式 'FILE.s' 组合语言档 'FILE.S' 需作前置处理的组合语言档 接下来我们来看整体参数: -x LANGUAGE GCC 会以档桉字尾来判定是何种语言来作编译, 而本参数的作用为指定后续的档桉之语言种类, 不管其字尾为何,LANGUAGE的可能内容为 c objective-c c++ c-header cpp-output c++-cpp-output assembler assembler-with-cpp 这些指定的语言会一直作用到下一个-x参数 -x none 不指定任何语言,以输入档桉的字尾为准 当只希望作某阶段的编译时,用-x可指定编译的 起点,下述的参数可以指定编译终点: -E 此参数指定只作完perprocess便停止,不作接下 来compiler等动作,并输出到标准输出(stdout) 去,对于不需前置处理的档桉本参数无效。 -S 只进行到compiler便停止,因此输出为一组合语 言档,在内定的输出档名为输入档名去除.c或.i 等字尾之后加上.s而成。 -c 只 compile或assemble输入档桉,而不作连结, 因此输出为一目的档(object file), 内定的档 名为去除.c、.i或是.s等之后加上.o而成。 -o FILE 不管在那一阶段结束动作,都可用此参数将内定 的输出档名改成"FILE";若是没用此参数,最后 产生的可执行档档名内定为 "a.out",在各阶段 结束的输出内定档名请看上列参数。 -v 将正在执行的编译阶段及编连程式的版本输出至 标准错误输出(standard error output)。 -pipe 在不同阶段之编译时,使用管线(pipe)取代暂存 档,在某些系统上的组译程式(assembler) 由于 不能由管线读取资料,而使得这参数无效,但对 GNU assembler 而言此参数当然可用。 和C之方言(C Dialect)有关的参数 ============================== 所谓的「方言」或是「标准语言」在此是以 制定的标准而言,因此ANSI C便是「标准语言」 ,而相对的较旧(在制定之前的 C)或是较新者( 如C++)便成了「方言」,这样的分类是因为依实 际的情况而定,没半点优劣的意思。 对于 C++语言而言,虽然 gcc能以档名字尾 为判断而作正确的编连,但有时仍需指定物件程 式库libg++为连结所需程式库,因此如果要编连 C++语言,建议使用 g++(某些系统上为 c++)来 作,因为 g++会将语言内定为 C++、将libg++内 定为标准程式库,而且对于字尾并不符合标准字 尾的档桉也能作编连。 接下来我们来看方言参数: -ansi 指定为ANSI C语言,本参数会将不符合ANSI C标 准的字视为语法错误,因此本参数会关闭 GCC中 许多有用的关键字,如 asm、inline和typeof, 以及用来辨认系统的巨集名称如unix和 vax,而 且 $将不可用来作为识别字的一部份;__asm__ 、__extension__、__inline__ 和__typeof__这 四个字不管有无本参数都可继续使用,不过它们 通常是放在标头档中,而 __unix__ 和 __vax__ 也是相同情况;当指定本参数时,alloca,abort exit、_exit 和 ffs将不会视为内建函数。 当设定本参数时,将会定义__STRICT_ANCI__ 这 个巨集,许多标头档利用这个巨集定义与否来宣 告相关的其它巨集或函数,这个目的只是为了符 合ANSI的标准。 对于非ANSI的程式,本参数并不会使编译终结或 发出任何警告,因此通常需加入 -pedantic,有 关这个参数将在警告参数中说明。 -fon-asm 不将 asm、inline和typeof识为保留字,因此这 些字可当成识别字(identifiers), 如欲达到这 些功能,可用__asm__,__inline__和__typeof__ 来代替;而 -ansi包含本参数的功能。 -fno-builtin 对于某些不是以双底线(__)开头的函数,不视为 内建函数,这些函数为--abort、abs、alloca、 cos、exit、fabs、ffs、labs、memcmp、memcpy 、sin、sqrt、strcmp、strcpy和strlen。 通常 GCC对这些函数有特定的处理方式--利用一 段小而且更有效率的码来取代,例如呼叫alloca 通常会变成直接调整堆叠的单一指令,而memcpy 则是一inline的複製迴圈,这使得程式变小又变 快,由于程式码已经改变,因此无法对这类函数 定除错中断点,而且也不能以连结别的程式库的 方式来改变这些函数的行为。 -trigraphs 支援ANSI C trigraphs,本参数包含在-ansi中. -traditional 会使 GCC以「传统」的方式来编译程式,所谓传 统是指在ANSI C之前,其方式如下: ○所有在函数内宣告的extern变数具有整体作用 ○新的保留字'typeof'、'inline'、'signed'、 'const' 和'volatile'不具作用 ○允许指标和整数作比较。 ○'unsigned short'和 'unsigned char'皆转换 成'unsigned int'。 ○不认定超出范围的浮点数指标为错误 ○对于 constants型态的字串,会存放在可写入 的区域,可在程式中更改其内容。 ○不再定义'__STDC__',然而'__GNUC__'仍将定 义,这使得在标头档中能以这两个巨集名称存 在与否来分辨是用 GNU C、traditional GNU C 、other ANSI C或是other old C 编译程式 来作编译。 -traditional-cpp 以传统的 C前置处理器方式来编译程式。 ○以 newline字元为一字串的结束记号。 ○将'\x'和'\a'两escape sequences转换成'x' 和 'a'字元。 ○允许'this'在 C++程式中使用。 以上这三项方式包含在-traditional参数中。 -fcond-mismatch 允许 conditional expressions之第二和第三个 参数为mismatched types。 -funsigned-char -fno-signed-char 将'char'设为unsigned;不同的主机和系统对于 'char'的基本设定并不相同,有些为signed、其 它则相反,程式中的'char'变数会因不同系统而 有差异,本参数主要目的便是作强制设定;本参 数之同义词为-fno-signed-char。 -fsigned-char -fno-unsigned-char 将'char'设为signed。 -fsigned-bitfields -funsigned-bitfields -fno-signed-bitfields -fno-unsigned-bitfields 这四个参数主要目的为设定 bitfields之符号值 ,在内定上 bitfields是为signed--这可从基本 资料如 'int'是为signed看出;在-traditional 下所有的bitfields全为unsigned。 -fwritable-strings 对于 constants型态的字串,会存放在可写入之 区域,可在程式中更改其内容;-traditional包 含本项功能。 -fallow-single-precision 就算是使用-traditional的情况下,也不要将单 精密度的运算子强制成双精密度;传统的 K&R编 译程式会不论运算子之型态而将运算式改成双精 密度,然而编译时单精密度比双精密度快多了, 因此若是需用-traditional又不需要double的运 算,便可用本参数;对于ANSI C和 GNU C而言本 参数无效。 C++ 方言参数 ============ 以下是用来控制编译 C++时的一些参数,虽 然这些参数只对 C++有作用,然而若是使用于其 它语言,将会被忽略而不会产生任何错误。 -fall-virtual 除了物件建构者(constructor) 和 new、delete 外,将其它出现的成员函数(member functions) 视为 virtual函数;这并非意谓每次都是透过内 部的 virtual函数表来呼叫这些成员函数,而是 若compiler判断能直接呼叫 virtual时,这些成 员函数同样也是直接呼叫。 -fdollars-in-identifiers 允许 $这个符号在识别字串(identifiers) 中使 用,传统的 C允许 $在识别字串中,但ANSI C和 C++禁止此种用法;本参数有相反词。 -felide-constructors 在允许的情况下省略建构函数,在下列的程式片 段中本参数会使得呼叫 foo()时直接初始化 y: A foo(); A y = foo(); 未使用本参数时 GCC之过程如下(一)呼叫物件A 的建构函数来初始化 y (二)将 foo()的结果存 到暂存区 (三)将暂存区的值代换给 y;这个过 程是ANSI C++的标准。 -fenum-int-equiv 允许 int型态完全的转换至enum。 -fexternal-templates 对于template函数将只在其定义的所在产生一份 copy,因而对template宣告能获得较小的程式码 ,然而要达到这个效果,除了本参数外,还要定 义#pragma implementation或是#progma inter- face;本参数会将所有的template视为外部函数 (external),因此可用 typedef来安排在在im- plementation file 中出现的instantiations; 对于本身并无template的source,无需顾虑相关 程式是否有template,皆不需本参数作编译,这 样只是会加大物件程式码的大小而以;本参数亦 有加上 no-之相反词。 -fmemoize-lookups -fsave-memoized 利用heuristics方法来加速编译,然而本方法未 成为内定的编译方式是因为本法只能针对一个输 入档桉,对其它档桉之速度反而变慢。 -fno-strict-prototype 在 C++中对函数的雏型宣告(prototype declar) 有严格的规定,例如 "int foo()"之宣告在 C中 是可以的,它代表 foo()并无输入值,但在 C++ 中得要宣告成 "int foo(void)",本参数便是允 许在 C++中作 C般的宣告。 -fnonnull-objects 设定references之物件为非null型态。 -fthis-is-varible 允许程式中使用this,这是为了和过去的程式相 容的参数。 -nostdinc++ 不要在内定的 C++标准目录下搜寻标头档,然而 其它标准目录还是依旧会寻找,本参数通常用于 建立 C++程式库libg++时。 -traditional 本参数对 C++的作用如同 -fthis-is-varible参 数(对C和C++的共同影响请参阅「C的方言参数」 中之-traditional项)。 除上所述之外,专对 C++之最佳化、警告和 程式码生成参数也一并在下讨论: -fno-default-inline 在 class中忽略inline之作用。 -Wenum-clash -Woverloased-virtual -Wtemplate-debugging 对于这些情况,只对 C++程式提出警告(相关内 容请看「警告参数」节)。 +eN 限定已定义之virtual函数如何使用(相关内容请 看「程式码生成参数」节)。 除错参数 ======== 对于编译后的可执行档之除错方式,通常的 方是是在编译之时便在特定部份放入除错码,有 除错码的程式在大小和执行速度上都会略逊一般 程式一筹,但相对的却也提供额外的讯息让 gdb 等除错程式进行对原始码及变数的追踪等工作, 除错码的规格有许多种,而 gdb也有自己独特的 部份,因此在不同的系统和不同的除错器下,便 需不同的参数才能顺利工作。 -g 产生作业系统认可格式(native format)的除错 码(可能为stabs、COFF、XCOFF或是 DWARF之一) ,以便 GDB除错。大多数的系统使用 stabs格式 ;本参数除了会产生标准格式码外,还会产生额 外的除错码,这些额外的部份只有 GDB能辨识, 目的是为了提供更多讯息,因此若是要以别的除 错程式来除错,便需使用 -gstabs+、-gstabs、 -gxcoff+、-gxcoff、-gdwarf+或是-gdwarf,这 些参数的作用请看下列依次的说明。 -ggdb 除了产生 native format的除错码外,还再包含 GDB专用的扩充部份。 -gstabs 产生 stabs格式除错码,但不包含 GDB的扩充部 份,这是在大多数 BSD系统上 DBX所使用的格式 ,然而在MIPS和 Alpha系统上却会产生 DBX所不 能辨识的内嵌(embedded) stabs除错码。 -gstabs+ 产生包含 GDB扩充部份的 stabs格式除错码,然 而这样别的除错程式便无法用来除错。 -gcoff 产生COFF格式的除错码;COFF格式是System V至 Release 4 之前的 SDB所用之格式。 -gxcoff 产生 XCOFF格式之除错码;这个格式为DBX在IBM RS/6000 系统上所使用。 -gxcoff+ 产生包含 GDB扩充部份的 XCOFF格式除错码。 -gdwarf 产生 DWARF格式的除错码;这个格式是System V Release 4 上 SDB所用之格式。 -gdwarf+ 产生包含 GDB扩充部份的 DWARF格式除错码。 -gLEVEL -ggdbLEVEL -gstabsLEVEL -gcoffLEVEL -gxcoffLEVEL -gdwarfLEVEL 产生不同格式的除错码,并依 LEVEL的数值决定 除错码的多寡,内定数值为 2。 LEVEL 1 产生最少量的除错码,通常置于非主要 除错部份,在这些除错码中包含函数和外部变数 (external variables)的描述,但无行编号和区 域变数(local variables)。 LEVEL 3 包含比 LEVEL 2更多额外资讯,如所有 此程式中定义的巨集。 -p 产生分析程式prof所能确认的特殊码,在编译和 连结时都需使用本参数才能达到目的。 -pg 产生分析程式 gprof所能确认的特殊码,在编译 和连结时都需使用本参数才能达到目的。 -a 产生basic blocks的基本讯息,如每个blocks执 行的时间、启始位址和包含的函数名称;如果-g 一起使用时,行编号和第一个 block的档名也会 一併记录,这些资料内定会附加到bb.out中,可 用tcov或是 gprof作分析。 -dLETTERS 本参数是对"编译程式"作 debug,方式为当编译 进行到 LETTERS所指定的步骤时,将特定资讯倾 印到指定的档桉去,相信读者看到此可能会觉得 奇怪,为何要对编译程式除错呢?照说 GCC应该 是不太可能会出错的,事实上别忘了 GCC是网路 上大家同心撰写出来的,新增改进部份的测试方 式为令其编译一程式并观查其结果,这时本参数 便派上用场了,有时也会发生 patch后再编译旧 程式时竟然出现过去没有的错误,若确定旧程式 没问题,可用本参数一步一步追踪编译的结果; 以下是 LETTERS的部份; M 当前置处理完毕后dump出所有的巨集定义 N 当前置处理完毕后dump出所有的巨集名称 D 当前置处理完毕后dump出所有的巨集定义至一 般输出,因此可重新将结果导至档桉 y 在语法分析(parsing) 时将除错讯息dump至标 准错误输出 r 在 RLT产生后将内容dump至FILE.rtl x 只产生 RLT码后便停止,本项通常和上一项 r 一起使用 j 第一轮跳跃最佳化作完便dump至FILE.jump s 完成 CSE后dump至FILE.cse L 当迴圈最佳化(loop optimization)完成后便 dump至FILE.loop t 第二轮 CSE完成后dump至FILE.cse2 f flow analysis 完成后dump至FILE.flow c 指令连结完成后dump至FILE.combine S 第一轮指令排程语法分析完成后后dump至 FILE.sched l 区域暂存器变数定位完成后dump至FILE.lreg g 全体暂存器变数定位完成后dump至FILE.greg R 第二轮指令排程语法分析完成后dump至 FILE.sched2 J 最后一轮最佳化完成后dump至FILE.jump2 d delayed branch 排程完成后dump至FILE.dbr k 暂存器变数转换到堆叠后dump至FILE.stack a 完成上列所有dump m 编译完毕后印出记忆体使用状况至std error p 将assember的输出加上有使用之alternative 和 pattern的注释 -fpretend-float 当进行cross-compiler时,将目标主机的浮点运 算器假设为和目前主机相同,以便编译出的程式 在目标主机上能执行无误;所谓cross-compiler 为在某一型主机或是OS上(如Unix)编译出另一OS (如 DOS)执行档的过程,如此在软体移植上更为 方便,相关的详细内容会另文再叙。 -save-temps 将编译时的产生的中间暂存档储存起来,例如使 用"-c -save-temps"来编译 foo.c时将会产生 foo.i 和 foo.s。 -print-libgcc-file-name 只是印出'libgcc.a'此档的绝对名称,在本参数 下 GCC并不作任何事--只是列出libgcc.a这个字 串;当使用 -nostdlib参数却同时想和libgcc.a 连结时便可依下列方式使用本参数: gcc -nostdlib FILES... 'gcc -print-libgcc- file-name' 结语 ==== 以上所介绍的为部份 GCC参数的用法,希望 能让读者对整个编译流程有助益,下次我们继续 探讨其馀的部份。 |

