gcc的命令大全

本文详细介绍了GCC编译器的使用方法,包括警告信息、C语言标准选择、生成特定格式文件、多文件编译与连接等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

 
gcc使用(附例子)
 
来源: ChinaUnix博客  日期: 2008.07.23 23:13 (共有条评论) 我要评论
 

                gcc使用(附例子)
                
                
                  
                        
                          
                          GCC 使用 
原帖链接
http://blog.chinaunix.net/u/13991/showart_96714.html

-------------------------------------------------------------------------------- 
1984年,Richard Stallman发起了自由软件运动,GNU (Gnu's Not Unix)项目应运而生,3年后,最初版GCC横空出世,成为第一款可移植、可优化、支持ANSI C开源C编译器。 
GCC最初全名是GNU C Compiler,之后,随着GCC支持语言越来越多,它名称变成了GNU Compiler Collection。 
这里介绍gccGCC前端,C编译器. 
警告信息 
-------------------------------------------------------------------------------- 
-Wall : 显示所有常用编译警告信息。 
-W : 显示更多常用编译警告,如:变量未使用、一些逻辑错误。 
-Wconversion : 警告隐式类型转换。 
-Wshadow : 警告影子变量(在代码块中再次声明已声明变量) 
-Wcast-qual :警告指针修改了变量修饰符。如:指针修改const变量。 
-Wwrite-strings : 警告修改const字符串。 
-Wtraditional : 警告ANSI编译器与传统C编译器有不同解释。 
-Werror : 即使只有警告信息,也不编译。(gcc默认:若只有警告信息,则进行编译,若有错误信息,则不编译) 
C语言标准 
-------------------------------------------------------------------------------- 
你可以在gcc命令行中通过指定选项来选择相应C语言标准: 从传统c到最新GNU扩展C. 默认情况下,gcc使用最新GNU C扩展. 
-ansi : 关闭GNU扩展中与ANSI C相抵触部分。 
-pedantic : 关闭所有GNU扩展。 
-std=c89 : 遵循C89标准 
-std=c99 : 遵循C99标准 
-std=traditional : 使用原始C 
注意:后4个选项可以与-ansi结合使用,也可以单独使用。 
可在gcc中使用大量GNU C扩展. 
生成特定格式文件 
-------------------------------------------------------------------------------- 
以hello.c为例子,可以设置选项生成hello.i, hello.s, hello.o以及最终hello文件: 
hello.c : 最初源代码文件; 
hello.i : 经过编译预处理源代码; 
hello.s : 汇编处理后汇编代码; 
hello.o : 编译后目标文件,即含有最终编译出机器码,但它里面所引用其他文件中函数内存位置尚未定义。 
hello / a.out : 最终可执行文件 
(还有.a(静态库文件), .so(动态库文件), .s(汇编源文件)留待以后讨论) 
如果你不通过-o指定生成可执行文件名,那么会默认生成a.out. 不指定生成文件名肯能覆盖你上次生成a.out. 
e.g. 
$gcc hello.c 
在不给gcc传递任何参数情况下, gcc执行默认操作: 将源文件编译为目标文件--> 将目标文件连接为可执行文件(名为a.out) --> 删除目标文件. 
-c生成.o文件时,默认生成与源代码主干同名.o文件。比如对应hello.c生成hello.o. 但也可在生成目标文件时指定目标文件名(注意同时要给出.o后缀): 
$gcc -c -o demo.o demo.c 
$gcc -Wall -c hello.c : 生成hello.o 
$gcc -Wall -c -save-temps hello.c: 生成hello.i, hello.s, hello.o 
注意-Wall 选项使用场合:仅在涉及到编译(即会生成.o文件时,用-Wall) 
多文件编译、连接 
-------------------------------------------------------------------------------- 
如果原文件分布于多个文件中:file1.c, file2,c 
$gcc -Wall file1.c file2.c -o name 
若对其中一个文件作了修改,则可只重新编译该文件,再连接所有文件: 
$gcc -Wall -c file2.c 
$gcc file1.c file2.o -c name 
注意:若编译器在命令行中从左向右顺序读取.o文件,则它们出现顺序有限制:含有某函数定义文件必须出现在含有调用该函数文件之后。好在GCC无此限制。 
编译预处理 
-------------------------------------------------------------------------------- 

上述hello.c为例, 要对它进行编译预备处理, 有两种方法: 在gcc中指定-E选项,
或直接调用cpp.gcc编译预处理命令程序为cpp,比较新版本gcc已经将cpp集成了,但仍提供了cpp命令. 可以直接调用cpp命令,
也可以在gcc中指定-E选项指定它只进行编译预处理. 
$gcc -E hello.c == $cpp hello.c 
上述命令马上将预处理结果显示出来. 不利于观看. 可采用-c将预处理结果保存: 
$gcc -E -c hello.i hello.c == $cpp -o hello.i hello.c 
注意, -c指定名称要给出".i"后缀. 
另外, gcc针对编译预处理提供了一些选项: 
(1)
除了直接在源代码中用 #define NAME来定义宏外,gcc可在命令行中定义宏:-DNAME(其中NAME为宏名), 也可对宏赋值:
-DNAME=value 注意等号两边不能有空格! 由于宏扩展只是一个替换过程,也可以将value换成表达式,但要在两边加上双括号:
-DNAME="statement" 
e.g. $gcc -Wall -DVALUE="2+2" tmp.c -o tmp 
如果不显示地赋值,如上例子,只给出:-DVALUE,gcc将使用默认值:1. 
(2) 除了用户定义宏外, 有一些宏是编译器自动定义,它们以__开头,运行: $cpp -dM /dev/null, 可以看到这些宏. 注意, 其中含有不以__开头非ANSI宏,它们可以通过-ansi选项被禁止。 
查看宏扩展 
1,
运行 $gcc -E test.c,gcc对test.c进行编译预处理,并立马显示结果. (不执行编译) 2, 运行 $gcc -c
-save-temps test.c,不光产生test.o,还产生test.i, test.s,前者是编译预处理结果, 后者是汇编结果.
利用Emacs查看编译预处理结果 
针对含有编译预处理命令代码,可以利用emacs方便地查看预处理结果,而不需执行编译,更为方便是,可以只选取一段代码,而非整个文件: 
1,选择想要查看代码 
2,C-c C-e (M-x c-macro-expand) 
这样,就自动在一个名为"Macroexpansion"buffer中显示pre-processed结果. 
生成汇编代码 
-------------------------------------------------------------------------------- 
使用"-S"选项指定gcc生成以".s"为后缀汇编代码: 
$gcc -S hello.c 
$gcc -S -o hello.s hello.c 
生成汇编语言格式取决于目标平台. 另外, 如果是多个.c文件, 那么针对每一个.c文件生成一个.s文件. 
包含头文件 
-------------------------------------------------------------------------------- 

程序中包含与连接库对应头文件是很重要方面,要使用库,就一定要能正确地引用头文件。一般在代码中通过#include引入头文件,
如果头文件位于系统默认包含路径(/usr/includes), 则只需在#include中给出头文件名字, 不需指定完整路径.
但若要包含头文件位于系统默认包含路径之外, 则有其它工作要做: 可以(在源文件中)同时指定头文件全路径.
但考虑到可移植性,最好通过-I在调用gcc编译命令中指定。 
下面看这个求立方小程序(阴影语句表示刚开始不存在): 
#include  
#include  
int main(int argc, char *argv[]) 
{ 
double x = pow (2.0, 3.0); 
printf("The cube of 2.0 is %f/n", x); 
return 0; 
} 
使用gcc-2.95来编译它(-lm选项在后面连接选项中有介绍, 这里只讨论头文件包含问题): 
$gcc-2.95 -Wall pow.c -lm -o pow_2.95 
pow.c: In function `main': 
pow.c:5: warning: implicit declaration of function `pow' 
程序编译成功,但gcc给出警告: pow函数隐式声明。 
$./pow_2.95 
The cube of 2.0 is 1.000000 
明显执行结果是错误,在源程序中引入头文件(#include ),消除了错误。 
不要忽略Warning信息!它可能预示着,程序虽然编译成功,但运行结果可能有错。故,起码加上"-Wall"编译选项!并尽量修正Warning警告。 
搜索路径 
首先要理解 #include和#include"file.h"区别: 
#include只在默认系统包含路径搜索头文件 
#include"file.h"首先在当前目录搜索头文件, 若头文件不位于当前目录, 则到系统默认包含路径搜索头文件. 
UNIX类系统默认系统路径为: 
头文件,包含路径: /usr/local/include/ or /usr/include/ 
库文件,连接路径: /usr/local/lib/ or /usr/lib/ 
对于标准c库(glibc或其它c库)头文件, 我们可以直接在源文件中使用#include 来引入头文件. 
如果要在源文件中引入自己头文件, 就需要考虑下面问题: 
1, 如果使用非系统头文件, 头文件和源文件位于同一个目录, 如何引用头文件呢? 
——
我们可以简单地在源文件中使用 #include "file.h", gcc将当前目录file.h引入到源文件. 如果你很固执,
仍想使用#include 语句, 可以在调用gcc时添加"-I."来将当前目录添加到系统包含路径.
细心朋友可能会想到: 这样对引用其它头文件会不会有影响? 比如,
#include之后紧接着一个#include, 它能正确引入math.h吗?
答案是: 没有影响. 仍然能正确引用math.h. 我理解是: "-I."将当前目录作为包含路径第一选择, 若在当前目录找不到头文件,
则在默认路径搜索头文件. 这实际上和#include"file.h"是一个意思. 
2, 对于比较大型工程, 会有许多用户自定义头文件, 并且头文件和.c文件会位于不同目录. 又该如何在.c文件中引用头文件呢? 
——
可以直接在.c文件中利用#include“/path/file.h", 通过指定头文件路径(可以是绝对路径,
也可以是相对路径)来包含头文件. 但这明显降低了程序可移植性. 在别系统环境下编译可能会出现问题.
所以还是利用"-I"选项指定头文件完整包含路径. 
针对头文件比较多情况, 最好把它们统一放在一个目录中, 比如~/project/include. 这样就不需为不同头文件指定不同路径. 如果你嫌每次输入这么多选项太麻烦, 你可以通过设置环境变量来添加路径: 
$C_INCLUDE_PATH=/opt/gdbm-1.8.3/include 
$export C_INCLUDE_PATH 
$LIBRART_PATH=/opt/gdbm-1.8.3/lib 
$export LIBRART_PATH 
可一次指定多个搜索路径,":"用于分隔它们,"."表示当前路径,如: 
$C_INCLUDE_PATH=.:/opt/gdbm-1.8.3/include:/net/include 
$LIBRARY_PATH=.:/opt/gdbm-1.8.3/lib:/net/lib 
(可以添加多个路径,路径之间用:相隔,.代表当前目录,若.在最前头,也可省略) 
当然,若想永久地添加这些路径,可以在.bash_profile中添加上述语句. 
3, 还有一个比较猥琐办法: 系统默认包含路径不是/usr/include或/usr/local/include么? 我把自己头文件拷贝到其中一个目录, 不就可以了么? 确可以这样, 如果你只想在你自己机器上编译运行这个程序话. 
前面介绍了三种添加搜索路径方法,如果这三种方法一起使用,优先级如何呢? 
命令行设置 > 环境变量设置 > 系统默认 
与外部库连接 
-------------------------------------------------------------------------------- 
前面介绍了如何包含头文件. 而头文件和库是息息相关, 使用库时, 要在源代码中包含适当头文件,这样才能声明库中函数原型(发布库时, 就需要给出相应头文件). 
和包含路径一样, 系统也有默认连接路径: 
头文件,包含路径: /usr/local/include/ or /usr/include/ 
库文件,连接路径: /usr/local/lib/ or /usr/lib/ 
同样地, 我们想要使用某个库里函数, 必须将这个库连接到使用那些函数程序中. 
有一个例外: libc.a或libc.so (C标准库,它包含了ANSI C所定义C函数)是不需要你显式连接, 所有C程序在运行时都会自动加载c标准库. 
除了C标准库之外库称之为"外部库", 它可能是别人提供给你, 也可能是你自己创建(后面有介绍如何创建库内容). 
外部库有两种:(1)静态连接库lib.a 
(2)共享连接库lib.so 
两者共同点: 
.a, .so都是.o目标文件集合,这些目标文件中含有一些函数定义(机器码),而这些函数将在连接时会被最终可执行文件用到。 
两者区别: 

态库.a : 当程序与静态库连接时,库中目标文件所含所有将被程序使用函数机器码被copy到最终可执行文件中. 静态库有个缺点:
占用磁盘和内存空间. 静态库会被添加到和它连接每个程序中, 而且这些程序运行时, 都会被加载到内存中. 无形中又多消耗了更多内存空间. 

享库.so : 与共享库连接可执行文件只包含它需要函数引用表,而不是所有函数代码,只有在程序执行时,
那些需要函数代码才被拷贝到内存中, 这样就使可执行文件比较小,
节省磁盘空间(更进一步,操作系统使用虚拟内存,使得一份共享库驻留在内存中被多个程序使用).共享库还有个优点: 若库本身被更新,
不需要重新编译与 它连接源程序。 
静态库 
下面我们来看一个简单例子,计算2.0平方根(假设文件名为sqrt.c): 
#include  
#include  
int 
main (void) 
{ 
double x = sqrt (2.0); 
printf ("The square root of 2.0 is %f/n", x); 
return 0; 
} 
gcc将它编译为可执行文件: 
$gcc -Wall sqrt.c -o sqrt 
编译成功,没有任何警告或错误信息。执行结果也正确。 
$./sqrt 
The square root of 2.0 is 1.414214 
下面我们来看看刚才使用gcc版本: 
$gcc --version 
gcc (GCC) 4.0.2 20050808 (prerelease) (Ubuntu 4.0.1-4ubuntu9) 
现在我用2.95版gcc把sqrt.c再编译一次: 
$gcc-2.95 -Wall sqrt.c -o sqrt_2.95 
/tmp/ccVBJd2H.o: In function `main': 
sqrt.c:(.text+0x16): undefined reference to `sqrt' 
collect2: ld returned 1 exit status 

译器会给出上述错误信息,这是因为sqrt函数不能与外部数学库"libm.a"相连。sqrt函数没有在程序中定义,也不存在于默认C库
"libc.a"中,如果用gcc-2.95,应该显式地选择连接库。上述出错信息中"/tmp/ccVBJd2H.o"是gcc创造临时目标文件,
用作连接时用。 
使用下列命令可以成功编译: 
$gcc-2.95 -Wall sqrt.c /usr/lib/libm.a -o sqrt_2.95 
它告知gcc:在编译sqrt.c时,加入位于/usr/lib中libm.a库(C数学库)。 
C库文件默认位于/usr/lib, /usr/local/lib系统目录中; gcc默认地从/usr/local/lib, /usr/lib中搜索库文件。(在我Ubuntu系统中,C库文件位于/urs/lib中。 
这里还要注意连接顺序问题,比如上述命令,如果我改成: 
$gcc-2.95 -Wall /usr/lib/libm.asqrt.c-o sqrt_2.95 
gcc会给出出错信息: 
/tmp/cc6b3bIa.o: In function `main': 
sqrt.c:(.text+0x16): undefined reference to `sqrt' 
collect2: ld returned 1 exit status 
正如读取目标文件顺序,gcc也在命令行中从左向右读取库文件——任何包含某函数定义库文件必须位于调用该函数目标文件之后! 
指定库文件绝对路径比较繁琐,有一种简化方法,相对于上述命令,可以用下面命令来替代: 
$gcc-2.95 -Wall sqrt.c -lm -o sqrt_2.95 
其中"-l"表示与库文件连接,"m"代表"libm.a"中m。一般而言,"-lNAME"选项会使gcc将目标文件与名为"libNAME.a"库文件相连。(这里假设使用默认目录中库,对于其他目录中库文件,参考后面“搜索路径”。) 
上面所提到"libm.a"就是静态库文件,所有静态库文件扩展名都是.a! 
$whereis libm.a 
libm: /usr/lib/libm.a /usr/lib/libm.so 
正如前面所说,默认库文件位于/usr/lib/或/usr/local/lib/目录中。其中,libm.a是静态库文件,libm.so是后面会介绍动态共享库文件。 

果调用函数都包含在libc.a中(C标准库被包含在/usr/lib/libc.a中,它包含了ANSI
C所定义C函数)。那么没有必要显式指定libc.a:所有C程序运行时都自动包含了C标准库!(试试 $gcc-2.95 -Wall
hello.c -o hello)。 
共享库 
正因为共享库优点,如果系统中存在.so库,gcc默认使用共享库(在/usr/lib/目录中,库文件以共享和静态两种版本存在)。 
运行:$gcc -Wall -L. hello.c -lNAME -o hello 
gcc先检查是否有替代libNAME.so库可用。 
正如前面所说,共享库以.so为扩展名(so == shared object)。 
那么,如果不想用共享库,而只用静态库呢?可以加上 -static选项 
$gcc -Wall -static hello.c -lNAME -o hello 
它等价于: 
$gcc -Wall hello.c libNAME.a -o hello 
$ gcc-2.95 -Wall sqrt.c -static -lm -o sqrt_2.95_static 
$ gcc-2.95 -Wall sqrt.c -lm -o sqrt_2.95_default 
$ gcc-2.95 -Wall sqrt.c /usr/lib/libm.a -o sqrt_2.95_a 
$ gcc-2.95 -Wall sqrt.c /usr/lib/libm.so -o sqrt_2.95_so 
$ ls -l sqrt* 
-rwxr-xr-x 1 zp zp 21076 2006-04-25 14:52 sqrt_2.95_a 
-rwxr-xr-x 1 zp zp 7604 2006-04-25 14:52 sqrt_2.95_default 
-rwxr-xr-x 1 zp zp 7604 2006-04-25 14:52 sqrt_2.95_so 
-rwxr-xr-x 1 zp zp 487393 2006-04-25 14:52 sqrt_2.95_static 
上述用四种方式编译sqrt.c,并比较了可执行文件大小。奇怪是,-static -lm 和 /lib/libm.a为什么有区别?有知其原因着,恳请指明,在此谢谢了! :) 
如果libNAME.a在当前目录,应执行下面命令: 
$gcc -Wall -L. hello.c -lNAME -o hello 
-L.表示将当前目录加到连接路径。 
利用GNU archiver创建库 
$ar cr libhello.a hello_fn.o by_fn.o 
从hello_fn.o和by_fn.o创建libihello.a,其中cr表示:creat & replace 
$ar t libhello.a 
列出libhello.a中内容,t == table 
(也可创建libhello.so) 
关于创建库详细介绍,可参考本blogGNU binutils笔记 
调试 
-------------------------------------------------------------------------------- 

般地,可执行文件中是不包含任何对源代码参考,而debugger要工作,就要知道目标文件/可执行文件中机器码对应源代码信息(如:哪条语
句、函数名、变量名...).
debugger工作原理:将函数名、变量名,对它们引用,将所有这些对象对应代码行号储存到目标文件或可执行文件符 号表中。 
GCC提供-g选项,将调试信息加入到目标文件或可执行文件中。 
$gcc -Wall -g hello.c -o hello 
注意:若发生了段错误,但没有core dump,是由于系统禁止core文件生成! 
$ulimit -c  ,若显示为0,则系统禁止了core dump 
解决方法: 
$ulimit -c unlimited  (只对当前shell进程有效) 
或在~/.bashrc 最后加入: ulimit -c unlimited (一劳永逸) 
优化 
-------------------------------------------------------------------------------- 
GCC具有优化代码功能,代码优化是一项比较复杂工作,它可归为:源代码级优化、速度与空间权衡、执行代码调度。 
GCC提供了下列优化选项: 
-O0 : 默认不优化(若要生成调试信息,最好不优化) 
-O1 : 简单优化,不进行速度与空间权衡优化; 
-O2 : 进一步优化,包括了调度。(若要优化,该选项最适合,它是GNU发布软件默认优化级别; 
-O3 : 鸡肋,兴许使程序速度更慢; 
-funroll-loops : 展开循环,会使可执行文件增大,而速度是否增加取决于特定环境; 
-Os : 生成最小执行文件; 
一般来说,调试时不优化,一般优化选项用-O2(gcc允许-g与-O2联用,这也是GNU软件包发布默认选项),embedded可以考虑-Os。 
注意:此处为O!(非0或小写o,-o是指定可执行文件名)。 
检验优化结果方法:$time ./prog 
time测量指定程序执行时间,结果由三部分组成: 
real : 进程总执行时间, 它和系统负载有关(包括了进程调度,切换时间) 
user: 被测量进程中用户指令执行时间 
sys : 被测量进程中内核代用户指令执行时间 
user和sys和被称为CPU时间. 
注意:对代码优化可能会引发警告信息,移出警告办法不是关闭优化,而是调整代码。 
gcc编译-警告选项 
今天make程序时遇到了讨厌"warnings being treated as errors".查了一下,主要有以下几项: 
-pedantic选项,对于不符合ANSI/ISO C语言标准源代码产生相应警告信息 

是,-pedantic编译选项并不能保证被编译程序与ANSI/ISO
C标准完全兼容,它仅仅用来帮助Linux程序员离这个目标越来越近。换句话说,-pedantic选项能够帮助程序员发现一些不符合ANSI/ISO
C标准代码,但不是全部。事实上只有ANSI/ISO C语言标准中要求进行编译器诊断那些问题才有可能被gcc发现并提出警告。 
-Wall,它能够使gcc产生尽可能多警告信息 
-Werror,它要求gcc将所有警告当成错误进行处理,这在使用自动编译工具(如make等)时非常有用。如果编译时带上-Werror选项,那么gcc会在所有产生警告地方停止编译,迫使程序员对自己代码进行修
                
                
                

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/52475/showart_1090217.html
  发表评论 查看评论(共有条评论)
 
 

最新资讯更多>> 
 
· 金山卫士开源计划首周源码下载..
· 谷歌劝说诺基亚采用Android操作..
· 11月份Linux市场占有率升至5%
· Apache 基金会确认退出 JCP 执..
· Chrome 10 新功能探秘:新增GP..
· 金山宣布开源其安全软件
· 开源FTP服务器ProFTPD发现后门
· 女黑客在开源会议上抱受骚扰
· 21款值得关注Linux游戏
· 马化腾:腾讯半年后彻底转型,..
 
 
论坛热点更多>> 
 
· Linux系统移植从零开始!参与..
· 学习linux意义在哪里
· 使用netfilter在哪能获取到原..
· 哥纠结了
· 一个在线读开源代码工具,..
· 为什么我目录下没有.cshrc..
· 初学linux从哪里开始
· linux 系统无法上网
· 新手安装UCenter 时总是出错..
· cacti添加主机显示状态都是..
 
 
文档更新更多>> 
 
· 菜鸟入门三星ARM11嵌入式系统,是..
· 寻redhat 5.3 中文手册 for ia64
· 请问redhat 5.3 企业版用户手册..
· LINUX与UNIX SHELL编程指南(中文)
· 一些基本用户管理以及基本安装方法
· 菜鸟学习linux笔记与练习-----第..
· 菜鸟学习linux笔记与练习-----第..
· 服务器配置:Squid配置详解
· linux下u盘使用
· ubuntu dynamips 绑定网卡到虚拟机
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值