??xml version="1.0" encoding="utf-8" standalone="yes"?>
gcc and g++分别是gnu的c & c++~译?gcc/g++在执行编译工作的时候,d需??
1.预处?生成.i的文件[预处理器cpp]
2.预处理后的文g不{换成汇编语言,生成文g.s[~译器egcs]
3.有汇~变为目标代?机器代码)生成.o的文件[汇编器as]
4.q接目标代码,生成可执行程序[链接器ld]
[参数详解]
-x language filename
讑֮文g所使用的语a,使后~名无?对以后的多个有效.也就是根据约定C语言的后
~名称?c的,而C++的后~名是.C或?cpp,如果你很个性,军_你的C代码文g的后~
名是.pig 哈哈Q那你就要用q个参数,q个参数对他后面的文件名都v作用Q除非到?
下一个参数的使用?
可以使用的参数吗有下面的q些
`c', `objective-c', `c-header', `c++', `cpp-output', `assembler', and `a
ssembler-with-cpp'.
看到英文Q应该可以理解的?
例子用法:
gcc -x c hello.pig
-x none filename
x上一个选项Q也是让gccҎ文g名后~Q自动识别文件类?
例子用法:
gcc -x c hello.pig -x none hello2.c
-c
只激z预处理,~译,和汇~?也就是他只把E序做成obj文g
例子用法:
gcc -c hello.c
他将生成.o的obj文g
-S
只激z预处理和编译,是指把文g~译成ؓ汇编代码?
例子用法
gcc -S hello.c
他将生成.s的汇~代码,你可以用文本~辑器察?
-E
只激z预处理,q个不生成文?你需要把它重定向C个输出文仉?
例子用法:
gcc -E hello.c > pianoapan.txt
gcc -E hello.c | more
慢慢看吧,一个hello word 也要与处理成800行的代码
-o
制定目标名称,~省的时?gcc ~译出来的文件是a.out,很难?如果你和我有同感
Q改掉它,哈哈
例子用法
gcc -o hello.exe hello.c (?windows用习惯了)
gcc -o hello.asm -S hello.c
-pipe
使用道代替~译中时文?在用非gnu汇编工具的时?可能有些问题
gcc -pipe -o hello.exe hello.c
-ansi
关闭gnu c中与ansi c不兼容的Ҏ?Ȁzansi c的专有特?包括止一些asm inl
ine typeof关键?以及UNIX,vax{预处理?
-fno-asm
此选项实现ansi选项的功能的一部分Q它止asm,inline和typeof用作关键字?
-fno-strict-prototype
只对g++起作?使用q个选项,g++对不带参数的函?都认为是没有昑ּ的对参数
的个数和cd说明,而不是没有参?
而gcc无论是否使用q个参数,都将Ҏ有带参数的函?认ؓ城没有显式说明的cd
-fthis-is-varialble
是向传lc++看齐,可以使用this当一般变量?
-fcond-mismatch
允许条g表达式的W二和第三参数类型不匚w,表达式的值将为voidcd
-funsigned-char
-fno-signed-char
-fsigned-char
-fno-unsigned-char
q四个参数是对charcdq行讄,军_charcd讄成unsigned char(前两个参
?或?signed char(后两个参?
-include file
包含某个代码,单来?是便以某个文g,需要另一个文件的时?可以用它设
?功能q当于在代码中使用#include<filename>
例子用法:
gcc hello.c -include /root/pianopan.h
-imacros file
file文g的宏,扩展到gcc/g++的输入文?宏定义本wƈ不出现在输入文g?
-Dmacro
相当于C语言中的#define macro
-Dmacro=defn
相当于C语言中的#define macro=defn
-Umacro
相当于C语言中的#undef macro
-undef
取消对Q何非标准宏的定义
-Idir
在你是用#include"file"的时?gcc/g++会先在当前目录查找你所制定的头文g,?
果没有找?他回到缺省的头文件目录找,如果使用-I制定了目??
回先在你所制定的目录查?然后再按常规的顺序去?
对于#include<file>,gcc/g++会到-I制定的目录查?查找不到,然后到pȝ的缺
省的头文件目录查?
-I-
是取消前一个参数的功能,所以一般在-Idir之后使用
-idirafter dir
?I的目录里面查扑֤?讲到q个目录里面查找.
-iprefix prefix
-iwithprefix dir
一般一起??I的目录查扑֤?会到prefix+dir下查?
-nostdinc
使编译器不再pȝ~省的头文g目录里面扑֤文g,一般和-I联合使用,明确限定?
文g的位|?
-nostdin C++
规定不在g++指定的标准\l中搜烦,但仍在其他\径中搜烦,.此选项在创libg++?
使用
-C
在预处理的时?不删除注释信?一般和-E使用,有时候分析程序,用这个很方便?
-M
生成文g兌的信息。包含目标文件所依赖的所有源代码你可以用gcc -M hello.c
来测试一下,很简单?
-MM
和上面的那个一P但是它将忽略?include<file>造成的依赖关pR?
-MD
?M相同Q但是输出将导入?d的文仉?
-MMD
?MM相同Q但是输出将导入?d的文仉?
-Wa,option
此选项传递optionl汇~程?如果option中间有逗号,将option分成多个选项,?
后传递给会汇~程?
-Wl.option
此选项传递optionl连接程?如果option中间有逗号,将option分成多个选项,?
后传递给会连接程?
-llibrary
制定~译的时候用的?
例子用法
gcc -lcurses hello.c
使用ncurses库编译程?
-Ldir
制定~译的时候,搜烦库的路径。比如你自己的库Q可以用它制定目录,不然
~译器将只在标准库的目录找。这个dir是目录的名U?
-O0
-O1
-O2
-O3
~译器的优化选项?个别,-O0表示没有优化,-O1为缺省|-O3优化U别最高
-g
只是~译器,在编译的时候,产生调试信息?
-gstabs
此选项以stabs格式声称调试信息,但是不包括gdb调试信息.
-gstabs+
此选项以stabs格式声称调试信息,q且包含仅供gdb使用的额外调试信?
-ggdb
此选项尽可能的生成gdb的可以用的调试信息.
-static
此选项禁止用动态库Q所以,~译出来的东西,一般都很大Q也不需要什?
动态连接库Q就可以q行.
-share
此选项尽量用动态库Q所以生成文件比较小Q但是需要系l由动态库.
-traditional
试图让编译器支持传统的C语言Ҏ?
[参考资料]
-Linux/UNIX高~程
中科U旗软g技术有限公司编?清华大学出版Cև?
-Gcc man page
[ChangeLog]
-2002-08-10
ver 0.1 发布最初的文档
-2002-08-11
ver 0.11 修改文档格式
-2002-08-12
ver 0.12 加入了对静态库Q动态库的参?
-2002-08-16
ver 0.16 增加了gcc~译?个阶D늚命o
q行 gcc/egcs
**********q行 gcc/egcs***********************
GCC ?GNU ?C ?C++ ~译器。实际上QGCC 能够~译三种语言QC、C++ ?O
bject CQC 语言的一U面向对象扩展)。利?gcc 命o可同时编译ƈq接 C ?C++
源程序?
如果你有两个或少数几?C 源文Ӟ也可以方便地利用 GCC ~译、连接ƈ生成?
执行文g。例如,假设你有两个源文?main.c ?factorial.c 两个源文Ӟ现在要编
译生成一个计阶乘的E序?
代码:
-----------------------
清单 factorial.c
-----------------------
int factorial (int n)
{
if (n <= 1)
return 1;
else
return factorial (n - 1) * n;
}
-----------------------
清单 main.c
-----------------------
#include <stdio.h>
#include <unistd.h>
int factorial (int n);
int main (int argc, char **argv)
{
int n;
if (argc < 2)
{
printf ("Usage: %s n\n", argv [0]);
return -1;
}
else
{
n = atoi (argv[1]);
printf ("Factorial of %d is %d.\n", n, factorial (n));
}
return 0;
}
-----------------------
利用如下的命令可~译生成可执行文Ӟq执行程序:
$ gcc -o factorial main.c factorial.c
$ ./factorial 5
Factorial of 5 is 120.
GCC 可同时用来编?C E序?C++ E序。一般来_C ~译器通过源文件的后缀
名来判断?C E序q是 C++ E序。在 Linux 中,C 源文件的后缀名ؓ .cQ?C++ ?
文g的后~名ؓ .C ?.cpp。但是,gcc 命o只能~译 C++ 源文Ӟ而不能自动和 C
++ E序使用的库q接。因此,通常使用 g++ 命o来完?C++ E序的编译和q接Q该E?
序会自动调用 gcc 实现~译。假设我们有一个如下的 C++ 源文Ӟhello.CQ:
#include <iostream>
void main (void)
{
cout << "Hello, world!" << endl;
}
则可以如下调?g++ 命o~译、连接ƈ生成可执行文Ӟ
$ g++ -o hello hello.C
$ ./hello
Hello, world!
**********************gcc/egcs 的主要选项*********
gcc 命o的常用选项
选项 解释
-ansi 只支?ANSI 标准?C 语法。这一选项禁?GNU C 的某些特Ԍ
例如 asm ?typeof 关键词?
-c 只编译ƈ生成目标文g?
-DMACRO 以字W串?”定?MACRO 宏?
-DMACRO=DEFN 以字W串“DEFN”定?MACRO 宏?
-E 只运?C 预编译器?
-g 生成调试信息。GNU 调试器可利用该信息?
-IDIRECTORY 指定额外的头文g搜烦路径DIRECTORY?
-LDIRECTORY 指定额外的函数库搜烦路径DIRECTORY?
-lLIBRARY q接时搜索指定的函数库LIBRARY?
-m486 针对 486 q行代码优化?
-o FILE 生成指定的输出文件。用在生成可执行文g时?
-O0 不进行优化处理?
-O ?-O1 优化生成代码?
-O2 q一步优化?
-O3 ?-O2 更进一步优化,包括 inline 函数?
-shared 生成׃n目标文g。通常用在建立׃n库时?
-static 止使用׃nq接?
-UMACRO 取消?MACRO 宏的定义?
-w 不生成Q何警告信息?
-Wall 生成所有警告信息?/font>
]]>
开放、自由和灉|是Linux的魅力所在,而这一点在GCC上的体现是E序员通过它能够更好地控制整个~译q程。在使用GCC~译E序Ӟ~译q程可以被细分ؓ四个阶段Q?
?预处理(Pre-ProcessingQ?
?~译QCompilingQ?
?汇编QAssemblingQ?
?链接QLinkingQ?
Linux E序员可以根据自q需要让GCC在编译的M阶段l束Q以便检查或使用~译器在该阶D늚输出信息Q或者对最后生成的二进制文件进行控Ӟ以便通过加入不同数量和种cȝ调试代码来ؓ今后的调试做好准备。和其它常用的编译器一PGCC也提供了灉|而强大的代码优化功能Q利用它可以生成执行效率更高的代码?
GCC提供?0多条警告信息和三个警告别,使用它们有助于增强程序的E_性和可移植性。此外,GCCq对标准的C和C++语言q行了大量的扩展Q提高程序的执行效率Q有助于~译器进行代码优化,能够减轻~程的工作量?
GCCh
在学习用GCC之前Q下面的q个例子能够帮助用户q速理解GCC的工作原理,q将其立卌用到实际的项目开发中厅R首先用熟悉的编辑器输入清单1所C的代码Q?
清单1Qhello.c
#include <stdio.h>
int main(void)
{
printf ("Hello world, Linux programming!n");
return 0;
}
然后执行下面的命令编译和q行q段E序Q?
# gcc hello.c -o hello
# ./hello
Hello world, Linux programming!
从程序员的角度看Q只需单地执行一条GCC命o可以了Q但从编译器的角度来看,却需要完成一pd非常J杂的工作。首先,GCC需要调用预处理E序 cppQ由它负责展开在源文g中定义的宏,q向其中插入?include”语句所包含的内容;接着QGCC会调用ccl和as处理后的源代码~译成目标代码;最后,GCC会调用链接程序ldQ把生成的目标代码链接成一个可执行E序?
Z更好地理解GCC的工作过E,可以把上q编译过E分成几个步骤单独进行,q观察每步的q行l果。第一步是q行预编译,使用-E参数可以让GCC在预处理l束后停止编译过E:
# gcc -E hello.c -o hello.i
此时若查看hello.cpp文g中的内容Q会发现stdio.h的内容确实都插到文g里去了,而其它应当被预处理的宏定义也都做了相应的处理。下一步是hello.i~译为目标代码,q可以通过使用-c参数来完成:
# gcc -c hello.i -o hello.o
GCC默认?i文g看成是预处理后的C语言源代码,因此上述命o自动蟩q预处理步骤而开始执行编译过E,也可以?x参数让GCC从指定的步骤开始编译。最后一步是生成的目标文g链接成可执行文gQ?
# gcc hello.o -o hello
在采用模块化的设计思想q行软g开发时Q通常整个E序是由多个源文件组成的Q相应地也就形成了多个编译单元,使用GCC能够很好地管理这些编译单元。假设有一个由foo1.c和foo2.c两个源文件组成的E序Qؓ了对它们q行~译Qƈ最l生成可执行E序fooQ可以用下面这条命令:
# gcc foo1.c foo2.c -o foo
如果同时处理的文件不止一个,GCC仍然会按照预处理、编译和链接的过E依ơ进行。如果深Iv来,上面q条命o大致相当于依ơ执行如下三条命令:
# gcc -c foo1.c -o foo1.o
# gcc -c foo2.c -o foo2.o
# gcc foo1.o foo2.o -o foo
在编译一个包含许多源文g的工E时Q若只用一条GCC命o来完成编译是非常费旉的。假N目中?00个源文g需要编译,q且每个源文件中都包?10000行代码,如果像上面那样仅用一条GCC命o来完成编译工作,那么GCC需要将每个源文仉重新~译一遍,然后再全部连接v来。很昄Q这h费的旉相当多,其是当用户只是修改了其中某一个文件的时候,完全没有必要每个文仉重新~译一遍,因ؓ很多已经生成的目标文件是不会改变的。要解决q个问题Q关键是要灵z运用GCCQ同时还要借助像Makeq样的工兗?
警告提示功能
GCC包含完整的出错检查和警告提示功能Q它们可以帮助LinuxE序员写出更加专业和优美的代码。先来读L?所C的E序Q这D代码写得很p糕Q仔l检查一下不难挑出很多毛病:
◆main函数的返回D声明为voidQ但实际上应该是intQ?
◆用了GNU语法扩展Q即使用long long来声?4位整敎ͼ不符合ANSI/ISO C语言标准Q?
◆main函数在终止前没有调用return语句?
清单2Qillcode.c
#include <stdio.h>
void main(void)
{
long long int var = 1;
printf("It is not standard C code!n");
}
下面来看看GCC是如何帮助程序员来发现这些错误的。当GCC在编译不W合ANSI/ISO C语言标准的源代码Ӟ如果加上?pedantic选项Q那么用了扩展语法的地方将产生相应的警告信息:
# gcc -pedantic illcode.c -o illcode
illcode.c: In function `main':
illcode.c:9: ISO C89 does not support `long long'
illcode.c:8: return type of `main' is not `int'
需要注意的是,-pedantic~译选项q不能保证被~译E序与ANSI/ISO C标准的完全兼容,它仅仅只能用来帮助LinuxE序员离q个目标来近。或者换句话_-pedantic选项能够帮助E序员发C些不W合 ANSI/ISO C标准的代码,但不是全部,事实上只有ANSI/ISO C语言标准中要求进行编译器诊断的那些情况,才有可能被GCC发现q提告?
除了-pedantic之外QGCCq有一些其它编译选项也能够生有用的警告信息。这些选项大多?W开_其中最有h值的当数-Wall了,使用它能够GCC产生可能多的警告信息:
# gcc -Wall illcode.c -o illcode
illcode.c:8: warning: return type of `main' is not `int'
illcode.c: In function `main':
illcode.c:9: warning: unused variable `var'
GCCl出的警告信息虽然从严格意义上说不能作是错误,但却很可能成为错误的栖n之所。一个优U的LinuxE序员应该尽量避免生警告信息,使自q代码始终保持z、优和健壮的特性?
在处理警告方面,另一个常用的~译选项?WerrorQ它要求GCC所有的警告当成错误q行处理Q这在用自动编译工P如Make{)旉常有用。如果编译时带上-Werror选项Q那么GCC会在所有生警告的地方停止~译Q迫使程序员对自q代码q行修改。只有当相应的警告信息消除时Q才可能编译过El朝前推q。执行情况如下:
# gcc -Wall -Werror illcode.c -o illcode
cc1: warnings being treated as errors
illcode.c:8: warning: return type of `main' is not `int'
illcode.c: In function `main':
illcode.c:9: warning: unused variable `var'
对LinuxE序员来ԌGCCl出的警告信息是很有价值的Q它们不仅可以帮助程序员写出更加健壮的程序,而且q是跟踪和调试程序的有力工具。徏议在用GCC~译源代码时始终带上-Wall选项Qƈ把它逐渐培养成ؓ一U习惯,q对扑և常见的隐式编E错误很有帮助?
在Linux 下开发Y件时Q完全不使用W三方函数库的情冉|比较见的,通常来讲都需要借助一个或多个函数库的支持才能够完成相应的功能。从E序员的角度看,函数库实际上是一些头文gQ?hQ和库文Ӟ.so或?aQ的集合。虽然Linux下的大多数函数都默认头文g攑ֈ/usr/include/目录下,而库文g则放?usr/lib/目录下,但ƈ不是所有的情况都是q样。正因如此,GCC在编译时必须有自q办法来查找所需要的头文件和库文件?
GCC采用搜烦目录的办法来查找所需要的文gQ?I选项可以向GCC的头文g搜烦路径中添加新的目录。例如,如果?home/xiaowp/include/目录下有~译时所需要的头文ӞZ让GCC能够利地找到它们,可以?I选项Q?
# gcc foo.c -I /home/xiaowp/include -o foo
同样Q如果用了不在标准位置的库文gQ那么可以通过-L选项向GCC的库文g搜烦路径中添加新的目录。例如,如果?home/xiaowp/lib/目录下有链接时所需要的库文件libfoo.soQؓ了让GCC能够利地找到它Q可以用下面的命oQ?
# gcc foo.c -L /home/xiaowp/lib -lfoo -o foo
值得好好解释一下的?l选项Q它指示GCC去连接库文glibfoo.so。Linux下的库文件在命名时有一个约定,那就是应该以lib三个字母开_׃所有的库文仉遵@了同L规范Q因此在?l选项指定链接的库文g名时可以省去lib三个字母Q也是说GCC在对-lfooq行处理Ӟ会自动去链接名ؓlibfoo.so的文件?
Linux下的库文件分Z大类分别是动态链接库Q通常?sol尾Q和静态链接库Q通常?a l尾Q,两者的差别仅在E序执行时所需的代码是在运行时动态加载的Q还是在~译旉态加载的。默认情况下QGCC在链接时优先使用动态链接库Q只有当动态链接库不存在时才考虑使用静态链接库Q如果需要的话可以在~译时加?static选项Q强制用静态链接库。例如,如果?/home/xiaowp/lib/目录下有链接时所需要的库文件libfoo.so和libfoo.aQؓ了让GCC在链接时只用到静态链接库Q可以用下面的命oQ?
# gcc foo.c -L /home/xiaowp/lib -static -lfoo -o foo
代码优化
代码优化指的是编译器通过分析源代码,扑և其中未辑ֈ最优的部分Q然后对光新进行组合,目的是改善程序的执行性能。GCC提供的代码优化功能非常强大,它通过~译选项-On来控制优化代码的生成Q其中n是一个代表优化别的整数。对于不同版本的GCC来讲Qn的取D围及其对应的优化效果可能q不完全相同Q比较典型的范围是从0变化???
~译时用选项-O可以告诉GCC同时减小代码的长度和执行旉Q其效果{h?O1。在q一U别上能够进行的优化cd虽然取决于目标处理器Q但一般都会包括线E蟩转(Thread JumpQ和延迟退栈(Deferred Stack PopsQ两U优化。选项-O2告诉GCC除了完成所?O1U别的优化之外,同时q要q行一些额外的调整工作Q如处理器指令调度等。选项-O3则除了完成所?O2U别的优化之外,q包括@环展开和其它一些与处理器特性相关的优化工作。通常来说Q数字越大优化的{高Q同时也意味着E序的运行速度快。许多LinuxE序员都喜欢使用-O2选项Q因为它在优化长度、编译时间和代码大小之间Q取得了一个比较理想的q炏V?
下面通过具体实例来感受一下GCC的代码优化功能,所用程序如清单3所C?
清单3Qoptimize.c
#include <stdio.h>
int main(void)
{
double counter;
double result;
double temp;
for (counter = 0; counter < 2000.0 * 2000.0 * 2000.0 / 20.0 + 2020; counter += (5 - 1) / 4)
{ temp = counter / 1979; result = counter; }
printf("Result is %lfn", result);
return 0;
}
首先不加M优化选项q行~译Q?
# gcc -Wall optimize.c -o optimize
借助Linux提供的time命oQ可以大致统计出该程序在q行时所需要的旉Q?
# time ./optimizeResult is 400002019.000000real 0m14.942suser 0m14.940ssys 0m0.000s
接下M用优化选项来对代码q行优化处理Q?
# gcc -Wall -O optimize.c -o optimize
在同L条g下再ơ测试一下运行时_
# time ./optimizeResult is 400002019.000000real 0m3.256suser 0m3.240ssys 0m0.000s
Ҏ两次执行的输出结果不隄出,E序的性能的确得到了很大幅度的改善Q由原来?4U羃短到?U。这个例子是专门针对GCC的优化功能而设计的Q因此优化前后程序的执行速度发生了很大的改变。尽GCC的代码优化功能非常强大,但作Z名优U的LinuxE序员,首先q是要力求能够手工编写出高质量的代码。如果编写的代码短,q且逻辑性强Q编译器׃会做更多的工作,甚至Ҏ用不着优化?
优化虽然能够l程序带来更好的执行性能Q但在如下一些场合中应该避免优化代码Q?
?E序开发的时?优化{高Q消耗在~译上的旉p长,因此在开发的时候最好不要用优化选项Q只有到软g发行或开发结束的时候,才考虑Ҏl生成的代码q行优化?
?资源受限的时?一些优化选项会增加可执行代码的体U,如果E序在运行时能够甌到的内存资源非常紧张Q如一些实时嵌入式讑֤Q,那就不要对代码进行优化,因ؓp带来的负面媄响可能会产生非常严重的后果?
?跟踪调试的时?在对代码q行优化的时候,某些代码可能会被删除或改写,或者ؓ了取得更佳的性能而进行重l,从而跟踪和调试变得异常困难?
调试
一个功能强大的调试器不仅ؓE序员提供了跟踪E序执行的手D,而且q可以帮助程序员扑ֈ解决问题的方法。对于LinuxE序员来ԌGDBQGNU DebuggerQ通过与GCC的配合用,为基于Linux的Y件开发提供了一个完善的调试环境?
默认情况下,GCC在编译时不会调试符h入到生成的二q制代码中,因ؓq样会增加可执行文g的大。如果需要在~译时生成调试符号信息,可以使用GCC ?g或?ggdb选项。GCC在生调试符hQ同样采用了分的思\Q开发h员可以通过?g选项后附加数???来指定在代码中加入调试信息的多少。默认的U别?Q?g2Q,此时产生的调试信息包括扩展的W号表、行受局部或外部变量信息。?Q?g3Q包含?中的所有调试信息,以及源代码中定义的宏。?Q?g1Q不包含局部变量和与行h关的调试信息Q因此只能够用于回溯跟踪和堆栈{储之用。回溯跟t指的是监视E序在运行过E中的函数调用历Ԍ堆栈转储则是一U以原始的十六进制格式保存程序执行环境的ҎQ两者都是经常用到的调试手段?
GCC产生的调试符号具有普遍的适应性,可以被许多调试器加以利用Q但如果使用的是GDBQ那么还可以通过-ggdb选项在生成的二进制代码中包含GDB专用的调试信息。这U做法的优点是可以方便GDB的调试工作,但缺Ҏ可能D其它调试器(如DBXQ无法进行正常的调试。选项-ggdb能够接受的调试别和-g是完全一LQ它们对输出的调试符h着相同的媄响?
需要注意的是,使用M一个调试选项都会使最l生成的二进制文件的大小急剧增加Q同时增加程序在执行时的开销Q因此调试选项通常仅在软g的开发和调试阶段使用。调试选项对生成代码大的影响从下面的Ҏq程中可以看出来Q?
# gcc optimize.c -o optimize
# ls optimize -l-rwxrwxr-x 1 xiaowp xiaowp 11649 Nov 20 08:53 optimize (未加调试选项)
# gcc -g optimize.c -o optimize
# ls optimize -l-rwxrwxr-x 1 xiaowp xiaowp 15889 Nov 20 08:54 optimize (加入调试选项)
虽然调试选项会增加文件的大小Q但事实上Linux中的许多软g在测试版本甚xl发行版本中仍然使用了调试选项来进行编译,q样做的目的是鼓q户在发现问题时自己动手解冻I是Linux的一个显著特艌Ӏ?
下面q是通过一个具体的实例说明如何利用调试W号来分析错误,所用程序见清单4所C?
清单4Qcrash.c
#include <stdio.h>
int main(void)
{
int input =0;
printf("Input an integer:");
scanf("%d", input);
printf("The integer you input is %dn", input);
return 0;
}
~译q运行上qC码,会生一个严重的D错误(Segmentation faultQ如下:
# gcc -g crash.c -o crash
# ./crashInput
an integer:10Segmentation fault
Z更快速地发现错误所在,可以使用GDBq行跟踪调试Q方法如下:
# gdb crashGNU gdb Red Hat Linux (5.3post-0.20021129.18rh)…?gdb)
当GDB提示W出现的时候,表明GDB已经做好准备q行调试了,现在可以通过run命o让程序开始在GDB的监控下q行Q?
(gdb) runStarting program: /home/xiaowp/thesis/gcc/code/crashInput an integer:10Program received signal SIGSEGV, Segmentation fault.0x4008576b in _IO_vfscanf_internal () from /lib/libc.so.6
仔细分析一下GDBl出的输出结果不隄出,E序是由于段错误而导致异怸止的Q说明内存操作出了问题,具体发生问题的地Ҏ在调?_IO_vfscanf_internal ( )的时候。ؓ了得到更加有价值的信息Q可以用GDB提供的回溯跟t命令backtraceQ执行结果如下:
(gdb) backtrace
#0 0x4008576b in _IO_vfscanf_internal () from /lib/libc.so.6
#1 0xbffff0c0 in ?? ()
#2 0x4008e0ba in scanf () from /lib/libc.so.6
#3 0x08048393 in main () at crash.c:11
#4 0x40042917 in __libc_start_main () from /lib/libc.so.6
跌输出l果中的前面三行Q从输出l果的第四行中不隄出,GDB已经错误定位到crash.c中的W?1行了。现在仔l检查一下:
(gdb) frame 3
#3 0x08048393 in main () at crash.c:1111 scanf("%d", input);
使用GDB提供的frame命o可以定位到发生错误的代码D,该命令后面跟着的数值可以在backtrace命o输出l果中的行首扑ֈ。现在已l发现错误所在了Q应该将
scanf("%d", input);改ؓscanf("%d", &input);
完成后就可以退出GDB了,命o如下Q?
(gdb) quit
GDB的功能远q不止如此,它还可以单步跟踪E序、检查内存变量和讄断点{?
调试时可能会需要用到编译器产生的中间结果,q时可以使用-save-temps选项Q让GCC预处理代码、汇~代码和目标代码都作为文件保存v来。如果想查生成的代码是否能够通过手工调整的办法来提高执行性能Q在~译q程中生成的中间文g会很有帮助Q具体情况如下:
# gcc -save-temps foo.c -o foo
# ls foo*foo foo.c foo.i foo.s
GCC 支持的其它调试选项q包?p?pgQ它们会剖析(ProfilingQ信息加入到最l生成的二进制代码中。剖析信息对于找出程序的性能瓉很有帮助Q是协助LinuxE序员开发出高性能E序的有力工兗在~译时加?p选项会在生成的代码中加入通用剖析工具QProfQ能够识别的l计信息Q? pg选项则生成只有GNU剖析工具QGprofQ才能识别的l计信息?
最后提醒一点,虽然GCC允许在优化的同时加入调试W号信息Q但优化后的代码对于调试本n而言是一个很大的挑战。代码在l过优化之后Q在源程序中声明和用的变量很可能不再用,控制也可能会突然蟩转到意外的地方,循环语句有可能因为@环展开而变得到处都有,所有这些对调试来讲都将是一场噩梦。徏议在调试的时候最好不使用M优化选项Q只有当E序在最l发行的时候才考虑对其q行优化?
上次的培训园C介绍了GCC的编译过E、警告提C功能、库依赖、代码优化和E序调试六个斚w的内宏V这期是最后的一部分内容?
在将源代码变成可执行文g的过E中Q需要经q许多中间步骤,包含预处理、编译、汇~和q接。这些过E实际上是由不同的程序负责完成的。大多数情况下GCC可以为LinuxE序员完成所有的后台工作Q自动调用相应程序进行处理?
q样做有一个很明显的缺点,是GCC在处理每一个源文gӞ最l都需要生成好几个临时文g才能完成相应的工作,从而无形中D处理速度变慢。例如,GCC 在处理一个源文gӞ可能需要一个时文件来保存预处理的输出、一个时文件来保存~译器的输出、一个时文件来保存汇编器的输出Q而读写这些时文件显焉要耗费一定的旉。当软g目变得非常庞大的时候,p在这上面的代价可能会变得很沉重?
解决的办法是Q用Linux提供的一U更加高效的通信方式—管道。它可以用来同时q接两个E序Q其中一个程序的输出被直接作ؓ另一个程序的输入Q这样就可以避免使用临时文gQ但~译时却需要消耗更多的内存?
在编译过E中使用道是由GCC?pipe选项军_的。下面的q条命o是借助GCC的管道功能来提高~译速度的:
# gcc -pipe foo.c -o foo
在编译小型工E时使用道Q编译时间上的差异可能还不是很明显,但在源代码非常多的大型工E中Q差异将变得非常明显?
文g扩展?
在用GCC的过E中Q用户对一些常用的扩展名一定要熟悉Qƈ知道其含义。ؓ了方便大家学习用GCCQ在此将q些扩展名罗列如下:
.c C原始E序Q?
.C C++原始E序Q?
.cc C++原始E序Q?
.cxx C++原始E序Q?
.m Objective-C原始E序Q?
.i 已经q预处理的C原始E序Q?
.ii 已经q预处理之C++原始E序Q?
.s l合语言原始E序Q?
.S l合语言原始E序Q?
.h 预处理文?标头文g)Q?
.o 目标文gQ?
.a 存档文g?
GCC常用选项
GCC作ؓLinux下C/C++重要的编译环境,功能强大Q编译选项J多。ؓ了方便大家日后编译方便,在此常用的选项及说明罗列出来如下:
-c 通知GCC取消链接步骤Q即~译源码q在最后生成目标文Ӟ
-Dmacro 定义指定的宏Q它能够通过源码中的#ifdefq行验;
-E 不经q编译预处理E序的输输送至标准输出Q?
-g3 获得有关调试E序的详l信息,它不能与-o选项联合使用Q?
-Idirectory 在包含文件搜索\径的L处添加指定目录;
-llibrary 提示链接E序在创建最l可执行文g时包含指定的库;
-O?O2?O3 优化状态打开Q该选项不能?g选项联合使用Q?
-S 要求~译E序生成来自源代码的汇编E序输出Q?
-v 启动所有警报;
-Wall 在发生警报时取消~译操作Q即警报看作是错误Q?
-Werror 在发生警报时取消~译操作Q即把报警当作是错误Q?
-w 止所有的报警?
结
GCC 是在Linux下开发程序时必须掌握的工具之一。本文对GCC做了一个简要的介绍Q主要讲qC如何使用GCC~译E序、生警告信息、调试程序和加快 GCC的编译速度。对所有希望早日跨入Linux开发者行列的人来_GCC是成ؓ一名优U的LinuxE序员的赯Uѝ?
二?nbsp;装蝲光标
SetCursor(AfxGetApp()->LoadStandardCursor(IDC_WAIT));
其中::SetCursor()是全局函数Q用来设|整个例E的光标参数是宏定义光标句柄。AfxGetApp ()是一个系l函敎ͼ它返回当前的一个CWinApp对象。其成员函数LoadStandardCursor()用来d一个系l指针,每一U系l指针的具体宏定义如下:
IDC_APPSTARTING 带小沙漏的标准箭?BR>IDC_ARROW 标准头
IDC_CROSS 十字光标Q用于定位)
IDC_HAND Windows 2000Q手?BR>IDC_HELP 带问L头
IDC_IBEAM I型标
IDC_ICON Obsolete for applications marked version 4.0 or later.
IDC_NO 止W号
IDC_SIZE Obsolete for applications marked version 4.0 or later. Use IDC_SIZEALL.
IDC_SIZEALL 十字头
IDC_SIZENESW 指向东北和西南的双向头
IDC_SIZENS 指向南和北的双向头
IDC_SIZENWSE 指向西北和东南的双向头
IDC_SIZEWE 指向东西的双向箭?BR>IDC_UPARROW 上箭?BR>IDC_WAIT 沙漏
三、获得主框架Q?/STRONG>
CMainFrame * pMainframe = (CMainFrame *) AfxGetApp()->m_pMainWnd;
.获取应用E序的实例句柄:
Example: HANDLE hInstance=AfxGetInstanceHandle();
获得应用E序ȝ口的指针Q?BR> Example: AfxGetMainWnd() ->ShowWindow(SW_SHOWMAXMIZED); //使程序最大化
四、重新徏立字体的代码
if(m_fontLogo.m_hObject)
m_fontLogo.Detach();
m_fontLogo.CreateFont(nHeight, 0, 0, 0, nWeight, bItalic, bUnderline,0,0,0,0,0,0, Name);
五、用指定颜色填充区域
dc.FillSolidRect(rect, ::GetSysColor(COLOR_3DFACE));
六、绘制立体字体效果的字体Q很值得一?BR>void CTestView::OnPaint()
{
CPaintDC dc(this); // device context for painting
CRect rect;
GetWindowRect(rect);
CFont m_fontLogo;
m_fontLogo.CreateFont(24, 0, 0, 0, FW_BOLD, true,
FALSE,0,0,0,0,0,0, "Arial");
CString m_LogoText;
m_LogoText=_T("Benlux Pro3D System");
dc.SetBkMode(TRANSPARENT);
CFont * OldFont = dc.SelectObject(&m_fontLogo);
// draw text in DC
COLORREF OldColor = dc.SetTextColor( ::GetSysColor( COLOR_3DHILIGHT));
rect.right = rect.Width();
rect.bottom = rect.Height();
rect.left = rect.top = 0;
dc.FillSolidRect(rect, ::GetSysColor(COLOR_3DFACE));
dc.DrawText( m_LogoText, rect + CPoint(1,1), DT_SINGLELINE | DT_LEFT | DT_VCENTER);
dc.SetTextColor( ::GetSysColor( COLOR_3DSHADOW));
dc.DrawText( m_LogoText, rect, DT_SINGLELINE | DT_LEFT | DT_VCENTER);
// restore old text color
dc.SetTextColor( OldColor);
// restore old font
dc.SelectObject(OldFont);
// Do not call CView::OnPaint() for painting messages
}
七、简单的消息索和抽取函数Q能够让pȝ响应其它操作
BOOL PeekAndPump()
{
static MSG msg;
while (::PeekMessage(&msg,NULL,0,0,PM_NOREMOVE)) {
if (!AfxGetApp()->PumpMessage()) {
::PostQuitMessage(0);
return FALSE;
}
}
return TRUE;
}
八、在你的E序中用动画光标替换默认的等待光?(ANI光标的?
HCURSOR m_hAniCursor=NULL;
BeginWaitCursor(); //begin wait cursor for api function
//load ani cursor from file in current path
TCHAR cursorPath[MAX_PATH]; GetModuleFileName(NULL,cursorPath,MAX_PATH);
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
char fname[_MAX_FNAME];
char ext[_MAX_EXT];
_splitpath(cursorPath, drive, dir, fname, ext );
sprintf(cursorPath,"%s%swait.ani",drive,dir); //ani cursor file name is wait.ani
m_hAniCursor= LoadCursorFromFile(cursorPath);
HCURSOR oldCursor;
if(m_hAniCursor != NULL)
oldCursor=SetCursor(m_hAniCursor);
for(long i=0;i<1000;i++)
Sleep(5);
oldCursor=NULL;
m_hAniCursor=NULL;
EndWaitCursor(); //end wait cursor for api function
九、如何限制编辑框中的准许字符
如果用户在编辑控件中只允许接收数字,可以使用一个标准的~辑控gq指
定新的创建标志ES_NUMBERS,它是Windows 95新增加的标志Q该标志限制 ~辑?BR>件只按收数字字符?BR>如果用户需要复杂的~辑控gQ可以用Microsoft 的屏蔽编辑控Ӟ它是一个很有用的OLE定制控g?BR> 如果希望不用OLE 定制控g自己处理字符Q可以派生一个CEdit cdƈ处理WM_CHAR消息Q然后从~辑控g中过滤出特定的字W。首先,使用ClassWizard 建立一?CEdit的派生类Q其ơ,在对话类中指定一个成员变量将~辑控g分类在OnInitdialog 中调用CWnd: : SubclassDlgItem .
//In your dialog class declaration (.H file )
private :
CMyEdit m_wndEdit ; // Instance of your new edit control .
//In you dialog class implementation (.CPP file )
BOOL CSampleDialog : : OnInitDialog ( )
{
//Subclass the edit lontrod .
m_wndEdit .SubclassDlgItem (IDC_EDIT,this );
?BR>}
使用ClassWizard处理WM_CHAR消息Q计nChar参量q决定所执行的操作,用户可以定是否修改、传送字W。下例说明了如何昄字母字符Q如果字W是字母字符Q则调用CWnd ; OnCharQ否则不调用OnChar.
//Only display alphabetic dharacters .
void CMyEdit : : OnChar (UINT nChar , UINT nRepCnt , UITN nFlags )
{
//Determine if nChar is an alphabetic character .
if (: : IsCharAlpha ( ( TCHAR) nChar ) )
CEdit : : OnChar (nChar, nRepCnt , nFlags );
}
如果要修改字W,则不能仅仅简单地用修改过的nChar调用CEdit : : OnChar。要修改一个字W,需要首先修改nCharQ然后用修改q的nChar调用CWnd: : DefWindowProc。下例说明了如何字W{变ؓ大写Q?BR>//Make all characters uppercase
void CMyEdit : : OnChar (UINT nChar , UINT nRepCnt , UINT nFlags )
{
//Make sure character is uppercase .
if (: : IsCharAlpha ( .( TCHAR) nChar)
nChar=: : CharUpper (nChar ) ;
//Bypass default OnChar processing and directly call default window proc.
DefWindProc (WM_CHAR, nChar , MAKELPARAM (nRepCnt , nFlags )) ;
}
十、串太长时如何在其末显CZ个省略号
调用CDC:: DrawTextq指定DT_END_ELLIPSIS标志Q这样就可以用小略号取代串末字符使其适合于指定的边界矩Ş。如果要昄路径信息Q指定DT_END_ELLIPSIS标志q省略号取代串中间的字符?BR>void CSampleView:: OnDraw (CDC* pDC)
{
CTestDoc* pDoc=GetDocument ();
ASSERT_VALID (pDoc);
//Add ellpsis to end of string if it does not fit
pDC->Drawtext (CString ("This is a long string"),
CRect (10, 10, 80, 30), DT_LEFT | DT_END_ELLIPSIS);
//Add ellpsis to middle of string if it does not fit
pDC->DrawText (AfxgetApp () ->m_pszhelpfilePath,
CRect (10, 40, 200, 60), DT_LEFT | DT_PATH_ELLIPSIS);
}
十一、如何实C个橡皮区矩Ş(ht迹矩Şq可Ud、羃攄矩Ş)
CRectTracker是一个很有用的类Q可以通过调用CRectTracker:: TrackRubberBand响应WM_LBUTTONDOWN消息来创Z个橡皮区矩Ş。下例表明用CRectTrackerUd和重|视H中的蓝色椭圆的大小是很Ҏ的事情?BR> 首先Q在文档cM声明一个CRectTracker数据成员Q?BR>class CTestDoc: Public CDocument
{?BR>public:
CRectTracker m_tracker;
?BR>};
其次Q在文档cȝ构造函C初始化CRectTracker 对象Q?BR>CTestDoc::CTestDoc()
{
m_tracker.m_rect.SetRect (10, 10, 300, 300);
m_tracker.m_nStyle=CRectTracker:: resizeInside |
CRectTracker:: dottedLine;
}
然后Q在视图cȝOnDraw函数中画椭圆和踪q矩形:
void CTestView::OnDraw(CDC* pDC)
{
CTestDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
//Select blue brush into device context.
CBrush brush (RGB (0, 0, 255));
CBrush* pOldBrush=pDC->SelectObject (&brush);
//draw ellipse in tracking rectangle.
CRect rcEllipse;
pDoc->m_tracker.GetTrueRect (rcEllipse);
pDC->Ellipse (rcEllipse);
//Draw tracking rectangle.
pDoc->m_tracker.Draw (pDC);
//Select blue brush out of device context.
pDC->SelectObject(pOldBrush);
}
最后,视图cM处理WM_LBUTTONDOWN消息Qƈ增加下述代码。该D代码根据鼠标击键情况可以拖放、移动或者重|椭圆的大小?/P>
void CTestView::OnLButtonDown(UINT nFlags, CPoint point)
{
//Get pointer to document.
CTestDoc* pDoc=GetDocument();
ASSERT_VALID (pDoc);
//If clicked on ellipse, drag or resize it. Otherwise create a
//rubber-band rectangle nd create a new ellipse.
BOOL bResult=pDoc->m_tracker.HitTest (point)!=
CRectTracker::hitNothing;
//Tracker rectangle changed so update views.
if (bResult)
{
pDoc->m_tracker.Track (this,point,TRUE);
pDoc->SetModifiedFlag ();
pDoc->UpdateAllViews (NULL);
}
else
pDoc->m_tracker.TrackRubberBand (this,point,TRUE);
CView::OnLButtonDown(nFlags, point);
}
十二、如何在临时目录创徏一个时文?/STRONG>
如果你要在时目录下创徏临时文gQ下面的代码能帮C的忙?BR>bool GetuniqueTempName (CString& strTempName)
{
strTempName="";
//Get the temporary files directory.
TCHAR szTempPath [MAX_PATH];
DWORD dwResult=:: GetTempPath (MAX_PATH, szTempPath);
if (dwResult==0)
return false;
//Create a unique temporary file.
TCHAR szTempFile[MAX_PATH];
UINT nResult=GetTempFileName (szTempPath, _T ("~ex"),0,szTempFile);
if (dwResult==0)
return false;
strTempName=szTempFile;
return true;
}
十三、如何限制窗口的最范?/STRONG>
要限制窗体的大小Q下面的代码能帮C的忙?BR>在CMainFrame中增加WM_GETMAXMININFO消息的处理函敎ͼ然后在这个函C写代码如?
//限制ȝ体的最高度和宽度
void CMainFrame::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
{
lpMMI->ptMinTrackSize.x=600;
lpMMI->ptMinTrackSize.y=400;
CNewFrameWnd::OnGetMinMaxInfo(lpMMI);
}
十四、怎样删除文g到回收站?/STRONG>
要删除文件到回收站,很简单。只要用SHFileOperation函数p了,下面的代码我ؓ你演CZq一个函数的用法。当然你可以直接拯C的项目中?BR>//删除文g到回收站?BR>//pszPath : 待删除的全\径文件名
//bDelete : TRUE 删除Q不Ud回收站,FALSE:Ud回收?BR>一?nbsp;//q回 : TRUE 删除成功 FALSE 删除p|
BOOL CDelFileToRecycleDlg::Recycle(LPCTSTR pszPath, BOOL bDelete/*=FALSE*/)
{
SHFILEOPSTRUCT shDelFile;
memset(&shDelFile,0,sizeof(SHFILEOPSTRUCT));
shDelFile.fFlags |= FOF_SILENT; // don"t report progress
shDelFile.fFlags |= FOF_NOERRORUI; // don"t report errors
shDelFile.fFlags |= FOF_NOCONFIRMATION; // don"t confirm delete
// Copy pathname to double-NULL-terminated string.
//
TCHAR buf[_MAX_PATH + 1]; // allow one more character
_tcscpy(buf, pszPath); // copy caller"s pathname
buf[_tcslen(buf)+1]=0; // need two NULLs at end
// Set SHFILEOPSTRUCT params for delete operation
shDelFile.wFunc = FO_DELETE; // REQUIRED: delete operation
shDelFile.pFrom = buf; // REQUIRED: which file(s)
shDelFile.pTo = NULL; // MUST be NULL
if (bDelete)
{ // if delete requested..
shDelFile.fFlags &= ~FOF_ALLOWUNDO; // ..don"t use Recycle Bin
}
else
{ // otherwise..
shDelFile.fFlags |= FOF_ALLOWUNDO; // ..send to Recycle Bin
}
return SHFileOperation(&shDelFile); // do it!
}
十五、内存泄漏检?/STRONG>
也许你已l知道,在C++和C语言中指针问题也是内存甌与释放是一个o人头疼的事情Q假如你甌了内存,但没有释放,q且你的E序需要长旉地运行,那么Q系l的资源逐渐减少Q当pȝ的资源全部被用完Ӟpȝ会崩溃。所以在开发程序的q程中一定要保证资源的完全释放。下面我们来介绍内存漏洞的检查?BR>CZ如下Q?BR>// do your memory allocations and deallocations...
CString s = "This is a frame variable";
#ifdef _DEBUG
CMemoryState oldMemState, newMemState, diffMemState;
oldMemState.Checkpoint();
#endif
// the next object is a heap object
CString* p = new CString( "Smith Alan 581_0215" );
delete p;
p=NULL;
#ifdef _DEBUG
newMemState.Checkpoint();
BOOL b=diffMemState.Difference(oldMemState, newMemState);
if (b)
{
AfxMessageBox( "Memory leaked! " );
}
#endif
Ҏ试验Q由于我们无法释放掉象int CString char 甌的变量。只能释放指针型的变量。而检内存时Q照样会出现内存泄漏现象。所以,q种内存方式局限性还是很大。因为我们无法释N指针型变量?/P>
]]>