一、gcc基本用法
使用gcc编译器时,必须给出一系列必要的调用参数和文件名称。不同参数的先后顺序对执行结果没有影响,只有在使用同类参数时的先后顺序才需要考虑。如果使用了多个 -L 的参数来定义库目录,gcc会根据多个 -L 参数的先后顺序来执行相应的库目录。
因为很多gcc参数都由多个字母组成,所以gcc参数不支持单字母的组合,Linux中常被叫短参数(short options),如 -dr 与 -d -r 的含义不一样。
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程序中的头文件包含两种情况:
#include <stdio.h> #include "stdio.h"
其中,使用尖括号(<>),预处理程序 cpp 在系统默认包含文件目录(如/usr/include)中搜索相应的文件;
使用双引号,预处理程序 cpp 首先在当前目录中搜寻头文件,如果没有找到,就到指定的 dirname 目录中去寻找。
在程序设计中,如果需要的这种包含文件分别分布在不同的目录中,就需要逐个使用 -I 选项给出搜索路径。
-Ldirname:将dirname所指出的目录加入到程序函数库文件的目录列表中,是在链接过程中使用的参数。在默认状态下,链接程序 ld 在系统默认路径中(如 /usr/lib)寻找所需要的库文件。这个选项告诉链接程序,首先到 -L 指定的目录中去寻找,然后到系统默认路径中寻找;如果函数库存放在多个目录下,就需要依次使用这个选项,给出相应的存放目录。
-lname:链接时装载名为 libname.a 的函数库。该函数库位于系统默认的目录或者由 -L 选项确定的目录下。例如,-lm 表示链接名为 libm.a 的数学函数库。
eg: 假定有一个程序名为 test.c 的C语言源代码文件,要生成一个可执行文件。
#include <stdio.h>
int main(void)
{
printf("Hello world/n");
return 0;
}
最简单的办法:gcc test.c -o test
首先,gcc需要调用预处理程序 cpp,由它负责展开在源文件中定义的宏,并向其中插入“#include”语句所包含的内容;接着,gcc调用 ccl 和 as,将处理后的源代码编译成目标代码;最后,gcc调用链接程序 ld,把生成的目标代码链接成一个可执行程序。因此,默认情况下,预编译、编译链接一次完成。
编译过程的分步执行:为了更好地理解gcc的工作过程,我们可以让在gcc工作的4个阶段中的任何一个阶段中停止下来。相关的参数有:
-E 预编译后停下来,生成后缀为 .i 的预编译文件。
-c 编译后停下来,生成后缀为 .o 的目标文件。
-S 汇编后停下来,生成后缀为 .s 的汇编源文件。
第一步:进行预编译,使用 -E 参数
gcc -E test.c -o test.i
查看 test.i 文件中的内容,会发现 stdio.h 的内容确实都插到文件里去了,而其他应当被预处理的宏定义也都做了相应的处理。
第二步:将 test.i 编译为目标代码,使用 -c 参数
gcc -c test.c -o test.o
第三步:汇编源文件
gcc -S test.c -o test.s
第四步:将生成的目标文件链接成可执行文件
gcc test.o - o test
对于稍微复杂的情况,比如有多个源代码文件、需要链接库或有其他比较特别的要求,就要给定适当的调用选项参数:
eg:
整个源代码程序由两个文件 testmain.c 和 testsub.c 组成,程序中使用了系统提供的数学库(所有与浮点相关的数学运算都必须使用数学库)。
gcc testmain.c testsub.c -lm -o test
其中,-lm 表示链接系统的数学库 libm.a 。
说明:
在编译一个包含许多源文件的工程时,若只用一条gcc命令来完成编译是非常浪费时间的。假如项目中有100个源文件需要编译,并且每个源文件中都包含一万行代码,如果像上面那样仅用一条gcc命令来完成编译工作,那么gcc需要将每个源文件都重新编译一遍,然后再全部链接起来。很显然,这样浪费的时间相当多,尤其是当用户只是修改了其中某个文件的时候,完全没有必要将每个文件都重新编译一遍,因为很多已经生成的目标文件是不会发生改变的。要解决这个问题,需要借助像make这样的工具(make在其他文章会有详解)。
二、警告功能设置
gcc包含完整的出错检查和警告提示功能,它们可以帮助Linux程序员写出更加专业的代码。
(1)-pedantic 选项

当gcc在编译不符合ANSI/ISO C 语言标准的源代码时,将产生相应的警告信息。

(2)-Wall 选项
除了 -pedantic 之外,gcc 还有一些其他编译选项,也能够产生有用的警告信息。这些选项大多以 -W 开头。其中最有价值的当数 -Wall 了,使用它能够使 gcc 产生尽可能多的警告信息。
gcc提供了大量的警告选项,对代码中可能存在的问题提出警告,通常可以使用-Wall来开启以下警告:
-Waddress -Warray-bounds (only with -O2) -Wc++0x-compat
-Wchar-subscripts -Wimplicit-int -Wimplicit-function-declaration
-Wcomment -Wformat -Wmain (only for C/ObjC and unless
-ffreestanding) -Wmissing-braces -Wnonnull -Wparentheses
-Wpointer-sign -Wreorder -Wreturn-type -Wsequence-point
-Wsign-compare (only in C++) -Wstrict-aliasing -Wstrict-overflow=1
-Wswitch -Wtrigraphs -Wuninitialized (only with -O1 and above)
-Wunknown-pragmas -Wunused-function -Wunused-label -Wunused-value
-Wunused-variable unused-function:警告声明但是没有定义的static函数;
unused- label:声明但是未使用的标签;
unused-parameter:警告未使用的函数参数;
unused-variable:声明但是未使用的本地变量;
unused-value:计算了但是未使用的值;
format:printf和scanf这样的函数中的格式字符串的使用不当;
implicit-int:未指定类型;
implicit-function:函数在声明前使用;
char- subscripts:使用char类作为数组下标(因为char可能是有符号数);
missingbraces:大括号不匹配;
parentheses: 圆括号不匹配;
return-type:函数有无返回值以及返回值类型不匹配;
sequence-point:违反顺序点的代码,比如 a[i] = c[i++];
switch:switch语句缺少default或者switch使用枚举变量为索引时缺少某个变量的case;
strict-aliasing=n:使用n设置对指针变量指向的对象类型产生警告的限制程度,默认n=3;只有在-fstrict-aliasing设置的情况下有效;
unknow-pragmas:使用未知的#pragma指令;
uninitialized:使用的变量为初始化,只在-O2时有效;以下是在-Wall中不会激活的警告选项:
cast-align:当指针进行类型转换后有内存对齐要求更严格时发出警告;
sign-compare:当使用signed和unsigned类型比较时;
missing-prototypes:当函数在使用前没有函数原型时;
packed:packed 是gcc的一个扩展,是使结构体各成员之间不留内存对齐所需的空间,有时候会造成内存对齐的问题;
padded:也是gcc的扩展,使结构体成员之间进行内存对齐的填充,会造成结构体体积增大.
unreachable-code:有不会执行的代码时.
inline:当inline函数不再保持inline时(比如对inline函数取地址);
disable-optimization:当不能执行指定的优化时.(需要太多时间或系统资源). 可以使用
-Werror时所有的警告都变成错误,使出现警告时也停止编译.需要和指定警告的参数一起使用.如果编译时带上 -Werror 选项,那么 gcc 会在所有产生警告的地方停止编译,迫使程序员对自己的代码进行修改。只有当相应的警告信息消除时,才可能将编译过程继续朝前推进。

gcc 给出的警告信息虽然从严格意义上说不能算作错误,但却和可能成为错误来源。一个优秀的程序员应该尽量避免产生警告信息,使自己的代码始终保持简洁、优美和鲁棒的特性。
三、库操作选项
在Linux下开发软件时,完全不使用第三方函数库的情况是比较少见的,通常来讲都需要借助一个或多个函数库的支持才能够完成相应的功能。
从程序员的角度看,函数库实际上就是一些头文件(.h)和库文件(.so 或 .a)的集合。虽然Linux下的大多数函数都默认将头文件放到 /usr/include/ 目录下,而库文件则放到 /usr/lib/ 目录下,但并不是所有的情况都是这样。正因如此,gcc 在编译时必须有自己的办法来查找所需要的头文件和库文件。常用的方法有:
(1) -I
可以向 gcc 的头文件搜索路径中添加新的目录。
(2) -L
如果使用了不在标准位置的库文件,那么可以通过 -L 选项向 gcc 的库文件搜索路径中添加新的目录。
(3) -l
Linux下的库文件在命名时有一个约定,就是应该以 lib 这3个字母开头,由于所有的库文件都遵循了同样的规范,因此在用 -l 选项指定链接的库文件名时可以省去 lib 这3个字母。例如,gcc 在对 -lfoo 进行处理时,会自动去链接名为 libfoo.so 的文件。
(4) -static
Linux下的库文件分为两大类,分别是:动态链接库(通常以 .so 结尾)和静态链接库(通常以 .a 结尾)。
两者的差别仅在程序执行时所需的代码是在运行时动态加载的,还是在编译时静态加载的。
默认情况下,gcc 在链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库。
如果需要的话,可以在编译时加上 -static 选项,强制使用静态链接库。
(5) -shared
生成一个共享的目标文件,它能够与其他的目标一起链接生成一个可执行的文件。
四、调试选项
1.GCC常用选项
| 选项 | 含义 |
|---|---|
| –help | |
| –target-help | 显示 gcc 帮助说明。‘target-help’是显示目标机器特定的命令行选项。 |
| –version | 显示 gcc 版本号和版权信息 。 |
| -o outfile | 输出到指定的文件。 |
| -x language | 指明使用的编程语言。允许的语言包括:c c++ assembler none 。 ‘none’意味着恢复默认行为,即根据文件的扩展名猜测源文件的语言。 |
| -v | 打印较多信息,显示编译器调用的程序。 |
| -### | 与 -v 类似,但选项被引号括住,并且不执行命令。 |
| -E | 仅作预处理,不进行编译、汇编和链接。如上图所示。 |
| -S | 仅编译到汇编语言,不进行汇编和链接。如上图所示。 |
| -c | 编译、汇编到目标代码,不进行链接。如上图所示。 |
| -pipe | 使用管道代替临时文件。 |
| -combine | 将多个源文件一次性传递给汇编器。 |
2.其他GCC选项
更多有用的GCC选项:
| 命令 | 描述 |
|---|---|
| -l library -llibrary | 进行链接时搜索名为library的库。例子: $ gcc test.c -lm -o test |
| -Idir | 把dir加入到搜索头文件的路径列表中。例子: $ gcc test.c -I…/inc -o test |
| -Ldir | 把dir加入到搜索库文件的路径列表中。例子: $ gcc -I/home/foo -L/home/foo -ltest test.c -o test |
| -Dname | 预定义一个名为name的宏,值为1。例子: $ gcc -DTEST_CONFIG test.c -o test |
| -Dname=definition | 预定义名为name,值为definition的宏。 |
| -ggdb -ggdblevel | 为调试器 gdb 生成调试信息。level可以为1,2,3,默认值为2。 |
| -g -glevel | 生成操作系统本地格式的调试信息。-g 和 -ggdb 并不太相同, -g 会生成 gdb 之外的信息。level取值同上。 |
| -s | 去除可执行文件中的符号表和重定位信息。用于减小可执行文件的大小。 |
| -M | 告诉预处理器输出一个适合make的规则,用于描述各目标文件的依赖关系。对于每个源文件,预处理器输出 一个make规则,该规则的目标项(target)是源文件对应的目标文件名,依赖项(dependency)是源文件中 #include引用的所有文件。生成的规则可以是单行,但如果太长,就用/’-换行符续成多行。规则 显示在标准输出,不产生预处理过的C程序。 |
| -C | 告诉预处理器不要丢弃注释。配合`-E’选项使用。 |
| -P | 告诉预处理器不要产生#line'命令。配合-E’选项使用。 |
| -static | 在支持动态链接的系统上,阻止连接共享库。该选项在其它系统上无效。 |
| -nostdlib | 不连接系统标准启动文件和标准库文件,只把指定的文件传递给连接器。 |
| Warnings | |
| -Wall | 会打开一些很有用的警告选项,建议编译时加此选项。 |
| -W -Wextra | 打印一些额外的警告信息。 |
| -w | 禁止显示所有警告信息。 |
| -Wshadow | 当一个局部变量遮盖住了另一个局部变量,或者全局变量时,给出警告。很有用的选项,建议打开。 -Wall 并不会打开此项。 |
| -Wpointer-arith | 对函数指针或者void *类型的指针进行算术操作时给出警告。也很有用。 -Wall 并不会打开此项。 |
| -Wcast-qual | 当强制转化丢掉了类型修饰符时给出警告。 -Wall 并不会打开此项。 |
| -Waggregate-return | 如果定义或调用了返回结构体或联合体的函数,编译器就发出警告。 |
| -Winline | 无论是声明为 inline 或者是指定了-finline-functions 选项,如果某函数不能内联,编译器都将发出警告。如果你的代码含有很多 inline 函数的话,这是很有用的选项。 |
| -Werror | 把警告当作错误。出现任何警告就放弃编译。 |
| -Wunreachable-code | 如果编译器探测到永远不会执行到的代码,就给出警告。也是比较有用的选项。 |
| -Wcast-align | 一旦某个指针类型强制转换导致目标所需的地址对齐增加时,编译器就发出警告。 |
| -Wundef | 当一个没有定义的符号出现在 #if 中时,给出警告。 |
| -Wredundant-decls | 如果在同一个可见域内某定义多次声明,编译器就发出警告,即使这些重复声明有效并且毫无差别。 |
| Optimization | |
| -O0 | 禁止编译器进行优化。默认为此项。 |
| -O -O1 | 尝试优化编译时间和可执行文件大小。 |
| -O2 | 更多的优化,会尝试几乎全部的优化功能,但不会进行“空间换时间”的优化方法。 |
| -O3 | 在 -O2 的基础上再打开一些优化选项:-finline-functions, -funswitch-loops 和 -fgcse-after-reload 。 |
| -Os | 对生成文件大小进行优化。它会打开 -O2 开的全部选项,除了会那些增加文件大小的。 |
| -finline-functions | 把所有简单的函数内联进调用者。编译器会探索式地决定哪些函数足够简单,值得做这种内联。 |
| -fstrict-aliasing | 施加最强的别名规则(aliasing rules)。 |
| Standard | |
| -ansi | 支持符合ANSI标准的C程序。这样就会关闭GNU C中某些不兼容ANSI C的特性。 |
| -std=c89 | |
| -iso9899:1990 | 指明使用标准 ISO C90 作为标准来编译程序。 |
| -std=c99 | |
| -std=iso9899:1999 | 指明使用标准 ISO C99 作为标准来编译程序。 |
| -std=c++98 | 指明使用标准 C++98 作为标准来编译程序。 |
| -std=gnu9x | |
| -std=gnu99 | 使用 ISO C99 再加上 GNU 的一些扩展。 |
| -fno-asm | 不把asm, inline或typeof当作关键字,因此这些词可以用做标识符。用 asm, __inline__和__typeof__能够替代它们。 -ansi' 隐含声明了-fno-asm’。 |
| -fgnu89-inline | 告诉编译器在 C99 模式下看到 inline 函数时使用传统的 GNU 句法。 |
| C options | |
| -fsigned-char | |
| -funsigned-char | 把char定义为有/无符号类型,如同signed char/unsigned char。 |
| -traditional | 尝试支持传统C编译器的某些方面。详见GNU C手册。 |
| -fno-builtin | |
| -fno-builtin-function | 不接受没有 _builtin 前缀的函数作为内建函数。 |
| -trigraphs | 支持ANSI C的三联符( trigraphs)。`-ansi’选项隐含声明了此选项。 |
| -fsigned-bitfields | |
| -funsigned-bitfields | 如果没有明确声明signed'或unsigned’修饰符,这些选项用来定义有符号位域或无符号位域。缺省情况下,位域是有符号的,因为它们继承的基本整数类型,如int,是有符号数。 |
| -Wstrict-prototypes | 如果函数的声明或定义没有指出参数类型,编译器就发出警告。很有用的警告。 |
| -Wmissing-prototypes | 如果没有预先声明就定义了全局函数,编译器就发出警告。即使函数定义自身提供了函数原形也会产生这个警告。这个选项 的目的是检查没有在头文件中声明的全局函数。 |
| -Wnested-externs | 如果某extern声明出现在函数内部,编译器就发出警告。 |
| C++ options | |
| -ffor-scope | 从头开始执行程序,也允许进行重定向。 |
| -fno-rtti | 关闭对 dynamic_cast 和 typeid 的支持。如果你不需要这些功能,关闭它会节省一些空间。 |
| -Wctor-dtor-privacy | 当一个类没有用时给出警告。因为构造函数和析构函数会被当作私有的。 |
| -Wnon-virtual-dtor | 当一个类有多态性,而又没有虚析构函数时,发出警告。-Wall会开启这个选项。 |
| -Wreorder | 如果代码中的成员变量的初始化顺序和它们实际执行时初始化顺序不一致,给出警告。 |
| -Wno-deprecated | 使用过时的特性时不要给出警告。 |
| -Woverloaded-virtual | 如果函数的声明隐藏住了基类的虚函数,就给出警告。 |
| Machine Dependent Options (Intel) | |
| -mtune=cpu-type | 为指定类型的 CPU 生成代码。cpu-type可以是:i386,i486,i586,pentium,i686,pentium4 等等。 |
| -msse | |
| -msse2 | |
| -mmmx | |
| -mno-sse | |
| -mno-sse2 | |
| -mno-mmx | 使用或者不使用MMX,SSE,SSE2指令。 |
| -m32 | |
| -m64 | 生成32位/64位机器上的代码。 |
| -mpush-args | |
| -mno-push-args | (不)使用 push 指令来进行存储参数。默认是使用。 |
| -mregparm=num | 当传递整数参数时,控制所使用寄存器的个数。 |
3.对于Linux程序员来讲,gdb(GNU Debugger)通过与 gcc 的配合使用,为基于Linux的软件开发提供了一个完善的调试环境。常用的有:
(1) -g 和 -ggdb
默认情况下,gcc 在编译时不会将调试符号插入到生成的二进制代码中,因为这样会增加可执行文件的大小。如果需要在编译时生成调试符号信息,可以使用 gcc 的 -g 或 -ggdb 选项。
gcc 在产生调试符号时,同样采用了分级的思路,开发人员可以通过在 -g 选项后附加数字1、2、3指定在代码中加入调试信息的多少。默认的级别是2(-g2),此时产生的调试信息包括:扩展的符号表、行号、局部或外部变量信息。
级别3(-g3)包含级别2中的所有调试信息以及源代码中定义的宏。
级别1(-g1)不包含局部变量和与行号有关的调试信息,因此只能够用于回溯跟踪和堆栈转储。
回溯追踪:指的是监视程序在运行过程中函数调用历史。
堆栈转储:则是一种以原始的十六进制格式保存程序执行环境的方法。
注意:使用任何一个调试选项都会使最终生成的二进制文件的大小急剧增加,同时增加程序在执行时的开销,因此,调试选项通常仅在软件的开发和调试阶段使用。
(2) -p 和 -pg
会将剖析(Profiling)信息加入到最终生成的二进制代码中。剖析信息对于找出程序的性能瓶颈很有帮助,是协助Linux程序员开发出高性能程序的有力工具。
(3) -save-temps
保存编译过程中生成的一些列中间文件。
##gcc test.c -o test -save-temps
除了生成执行文件test之外,还保存了test.i 和 test.s 中间文件,供用户查询调试。

被折叠的 条评论
为什么被折叠?



