﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-Impossible is nothing-文章分类-编译器</title><link>http://www.cppblog.com/apollo/category/961.html</link><description>　　爱过知情重醉过知酒浓
　　花开花谢终是空
　　缘份不停留像春风来又走
　　女人如花花似梦</description><language>zh-cn</language><lastBuildDate>Wed, 21 May 2008 08:19:35 GMT</lastBuildDate><pubDate>Wed, 21 May 2008 08:19:35 GMT</pubDate><ttl>60</ttl><item><title>把 gcc/g++ 集成到 Visual C++ 2005 中</title><link>http://www.cppblog.com/apollo/articles/8094.html</link><dc:creator>笑笑生</dc:creator><author>笑笑生</author><pubDate>Fri, 02 Jun 2006 13:10:00 GMT</pubDate><guid>http://www.cppblog.com/apollo/articles/8094.html</guid><wfw:comment>http://www.cppblog.com/apollo/comments/8094.html</wfw:comment><comments>http://www.cppblog.com/apollo/articles/8094.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/apollo/comments/commentRss/8094.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/apollo/services/trackbacks/8094.html</trackback:ping><description><![CDATA[
		<p>
				<font face="Courier New">在VC2005中，如何调用其它编译器编译cpp文件呢?</font>
		</p>
		<p>
				<font face="Courier New">在VC2005中不能编译单个.cpp文件，除非把它放在工程中。但是，我们可以通过调用外部工具实现编译单文件。</font>
		</p>
		<p>
				<font face="Courier New">下面以调用G++为例说明如何编译cpp文件。如果要编译单个.c文件，使用gcc即可，方法同理。</font>
		</p>
		<p>
				<font face="Courier New">首先获取Windows版的 GNU C++ Compiler (g++)。你可以通过安装 Cygwin 或者 MinGW 来获得 g++ for Windows.</font>
		</p>
		<p>
				<font face="Courier New">以MinGW为例，我们可以下载Dev C++, Dev C++目前最新版为4.9.9.2。安装 Dev C++的同时就安装了MinGW,其中包含了 G++ 3.4.2，</font>
		</p>
		<p>
				<font face="Courier New">假设 Dev C++ 安装在 D:\Dev-Cpp 目录下。</font>
		</p>
		<p>
				<font face="Courier New">下面我们在VC2005的菜单点击 “工具(Tools) -&gt; 外部工具(External Tools) -&gt; 添加(Add)”。</font>
		</p>
		<p>
				<font face="Courier New">分别填入:</font>
		</p>
		<p>
				<font face="Courier New">标题(Title): GNU C++ Compiler</font>
		</p>
		<p>
				<font face="Courier New">命令(Command): D:\Dev-Cpp\bin\g++.exe</font>
		</p>
		<p>
				<font face="Courier New">参数(Arguments): -Wall -o $(ItemFileName) $(ItemFileName)$(ItemExt) -time</font>
		</p>
		<p>
				<font face="Courier New">初始目录(Initial directory): $(ItemDir)</font>
		</p>
		<p>
				<font face="Courier New">再钩选下面的"使用输出窗口(Use Output window)".</font>
		</p>
		<p>
				<font face="Courier New">说明：如果你已经把D:\Dev-Cpp\bin\目录添加到系统环境变量中，那么 Command指定为g++.exe即可。Cygwin同理。 </font>
		</p>
		<p>
				<font face="Courier New">好，下面切换到main函数所在文件，点击工具(Tools)菜单下的“GNU C++ Compiler”</font>
		</p>
		<p>
				<font face="Courier New">如果编译没有出错，对于早期版本的g++而言，Output window是没有任何输出的。</font>
		</p>
		<p>
				<font face="Courier New">而对于较新版本的g++,将输出类似以下的信息:</font>
		</p>
		<p>
				<font face="Courier New"># cc1plus 0.67 0.22<br /># as 0.05 0.01<br /># collect2 0.36 0.19</font>
		</p>
		<p>
				<font face="Courier New">这是 -time 报告的编译过程中每个子过程所占用的CPU时间。</font>
		</p>
		<p>
				<font face="Courier New">当然，你还可以更改编译参数，使输出窗口输出其他编译信息。</font>
		</p>
		<p>
				<font face="Courier New">我们还可以通过添加外部工具来实现运行g++生成的程序的功能。步骤如下:</font>
		</p>
		<p>
				<font face="Courier New">在VC2005的菜单点击“工具(Tools) -&gt; 外部工具(External Tools) -&gt; 添加(Add)”。</font>
		</p>
		<p>
				<font face="Courier New">分别填入:</font>
		</p>
		<p>
				<font face="Courier New">标题(Title): Run as C++ Application</font>
		</p>
		<p>
				<font face="Courier New">命令(Command): %systemroot%\system32\cmd.exe</font>
		</p>
		<p>
				<font face="Courier New">参数(Arguments): /c $(ItemFileName).exe</font>
		</p>
		<p>
				<font face="Courier New">初始目录(Initial directory): $(ItemDir)</font>
		</p>
		<p>
				<font face="Courier New">不要钩选下面的“使用输出窗口”和“退出时关闭”。</font>
		</p>
		<p>
				<font face="Courier New">注意：cpp文件必须放在工程中，g++生成的程序才能被运行！</font>
		</p>
		<p>
				<br />
				<font face="Courier New">下面讲讲如何设置快捷键。</font>
		</p>
		<p>
				<font face="Courier New">点击菜单中的"工具-&gt;自定义",</font>
		</p>
		<p>
				<font face="Courier New">点击右下角的 "键盘",</font>
		</p>
		<p>
				<font face="Courier New">点击 "按快捷键" 下面的文本框,</font>
		</p>
		<p>
				<font face="Courier New">按下 Shift + Ctrl + F7, 如果这个快捷键已经分配给了其他的命令，更换一个即可。</font>
		</p>
		<p>
				<font face="Courier New">在"显示命令包含" 中填入“工具.外部工具6", 再</font>
				<font face="Courier New">点击 “分配” 按钮.</font>
		</p>
		<p>
				<font face="Courier New">点"确定"，再点“关闭”.</font>
		</p>
		<p>
				<font face="Courier New">这样就给 GNU C++ Compiler 分配了快捷键 Shift + Ctrl + F7</font>
		</p>
		<p>
				<font face="Courier New">注意刚才是在GNU C++ Compiler 位于 外部工具菜单的第6项的情况下，在"显示命令包含" 中填入“工具.外部工具6"！</font>
		</p>
		<p>
				<font face="Courier New">同理，我们可以给 Run as C++ Application 分配一个快捷键。</font>
		</p>
		<p>
				<font face="Courier New">再讲讲如何分配一个工具条。</font>
		</p>
		<p>
				<font face="Courier New">点击菜单中的"工具-&gt;自定义", 点击 "工具栏" 选项卡.</font>
		</p>
		<p>
				<font face="Courier New">点击“新建”,</font>
		</p>
		<p>
				<font face="Courier New">在工具栏名称中填入“g++”,</font>
		</p>
		<p>
				<font face="Courier New">切换到 "命令" 选项卡.</font>
		</p>
		<p>
				<font face="Courier New">点击 "类别" 中的 "工具",</font>
		</p>
		<p>
				<font face="Courier New">在右边的命令把 “外部命令6” 拖动到刚才建立的g++工具条上.</font>
		</p>
		<p>
				<font face="Courier New">先不要关闭 "自定义" 对话框.</font>
		</p>
		<p>
				<font face="Courier New">下面单击g++工具条上的 GNU C++ Compiler 按钮</font>
		</p>
		<p>
				<font face="Courier New">再点击自定义 对话框 中的 “修改选中的内容”按钮，设置工具条中按钮的名称和文字等属性。</font>
		</p>
		<p>
				<font face="Courier New">Run as C++ Application 也可以通过这种方式放到g++工具条中。</font>
		</p>
		<p>
				<br />
				<font face="Courier New">如果感兴趣，你还可以把make.exe等添加到VC2005的外部工具中，以实现编译整个工程的目标。当然，在工程中少不了makefile.</font>
		</p>
		<p>
				<font face="Courier New">GDB.exe也是可以集成进来的，不过不要忘了参数(Arguments)应为 -g -Wall -o $(ItemFileName) $(ItemFileName)$(ItemExt) -time，其中参数 -g 生成调试信息。GNU 调试器可利用该信息。</font>
		</p>
		<p>
				<font face="Courier New">经过改造，VC2005可以被打造成一个基于开源编译器和调试器的集成开发环境。具体后续步骤我会慢慢补充。<br /></font>
		</p>
<img src ="http://www.cppblog.com/apollo/aggbug/8094.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/apollo/" target="_blank">笑笑生</a> 2006-06-02 21:10 <a href="http://www.cppblog.com/apollo/articles/8094.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Gcc大全</title><link>http://www.cppblog.com/apollo/articles/GCCComplate.html</link><dc:creator>笑笑生</dc:creator><author>笑笑生</author><pubDate>Sat, 04 Mar 2006 12:08:00 GMT</pubDate><guid>http://www.cppblog.com/apollo/articles/GCCComplate.html</guid><wfw:comment>http://www.cppblog.com/apollo/comments/3720.html</wfw:comment><comments>http://www.cppblog.com/apollo/articles/GCCComplate.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/apollo/comments/commentRss/3720.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/apollo/services/trackbacks/3720.html</trackback:ping><description><![CDATA[<font color="#000000" face="Verdana" size="2"><br>gcc and g++分别是gnu的c &amp; c++编译器 gcc/g++在执行编译工作的时候，总共需要4步 <br><br>1.预处理,生成.i的文件[预处理器cpp] <br>2.将预处理后的文件不转换成汇编语言,生成文件.s[编译器egcs] <br>3.有汇编变为目标代码(机器代码)生成.o的文件[汇编器as] <br>4.连接目标代码,生成可执行程序[链接器ld] <br>[参数详解] <br>-x language filename <br>　 设定文件所使用的语言,使后缀名无效,对以后的多个有效.也就是根据约定C语言的后 <br>缀名称是.c的，而C++的后缀名是.C或者.cpp,如果你很个性，决定你的C代码文件的后缀 <br>名是.pig 哈哈，那你就要用这个参数,这个参数对他后面的文件名都起作用，除非到了 <br>下一个参数的使用。 <br>　　可以使用的参数吗有下面的这些 <br>　　`c', `objective-c', `c-header', `c++', `cpp-output', `assembler', and `a <br>ssembler-with-cpp'. <br>　　看到英文，应该可以理解的。 <br>　　例子用法: <br>　　gcc -x c hello.pig <br>　　 <br>-x none filename <br>　　关掉上一个选项，也就是让gcc根据文件名后缀，自动识别文件类型 <br>　　例子用法: <br>　　gcc -x c hello.pig -x none hello2.c <br>　　 <br>-c <br>　　只激活预处理,编译,和汇编,也就是他只把程序做成obj文件 <br>　　例子用法: <br>　　gcc -c hello.c <br>　　他将生成.o的obj文件 <br>-S <br>　　只激活预处理和编译，就是指把文件编译成为汇编代码。 <br>　　例子用法 <br>　　gcc -S hello.c <br>　　他将生成.s的汇编代码，你可以用文本编辑器察看 <br>-E <br>　　只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面. <br>　　例子用法: <br>　　gcc -E hello.c &gt; pianoapan.txt <br>　　gcc -E hello.c | more <br>　　慢慢看吧,一个hello word 也要与处理成800行的代码 <br>-o <br>　　制定目标名称,缺省的时候,gcc 编译出来的文件是a.out,很难听,如果你和我有同感 <br>，改掉它,哈哈 <br>　　例子用法 <br>　　gcc -o hello.exe hello.c (哦,windows用习惯了) <br>　　gcc -o hello.asm -S hello.c <br>-pipe <br>　　使用管道代替编译中临时文件,在使用非gnu汇编工具的时候,可能有些问题 <br>　　gcc -pipe -o hello.exe hello.c <br>-ansi <br>　　关闭gnu c中与ansi c不兼容的特性,激活ansi c的专有特性(包括禁止一些asm inl <br>ine typeof关键字,以及UNIX,vax等预处理宏, <br>-fno-asm <br>　　此选项实现ansi选项的功能的一部分，它禁止将asm,inline和typeof用作关键字。 <br>　　　　 <br>-fno-strict-prototype <br>　　只对g++起作用,使用这个选项,g++将对不带参数的函数,都认为是没有显式的对参数 <br>的个数和类型说明,而不是没有参数. <br>　　而gcc无论是否使用这个参数,都将对没有带参数的函数,认为城没有显式说明的类型 <br><br>　　 <br>-fthis-is-varialble <br>　　就是向传统c++看齐,可以使用this当一般变量使用. <br>　　 <br>-fcond-mismatch <br>　　允许条件表达式的第二和第三参数类型不匹配,表达式的值将为void类型 <br>　　 <br>-funsigned-char <br>-fno-signed-char <br>-fsigned-char <br>-fno-unsigned-char <br>　　这四个参数是对char类型进行设置,决定将char类型设置成unsigned char(前两个参 <br>数)或者 signed char(后两个参数) <br>　　 <br>-include file <br>　　包含某个代码,简单来说,就是便以某个文件,需要另一个文件的时候,就可以用它设 <br>定,功能就相当于在代码中使用#include&lt;filename&gt; <br>　　例子用法: <br>　　gcc hello.c -include /root/pianopan.h <br>　　 <br>-imacros file <br>　　将file文件的宏,扩展到gcc/g++的输入文件,宏定义本身并不出现在输入文件中 <br>　　 <br>-Dmacro <br>　　相当于C语言中的#define macro <br>　　 <br>-Dmacro=defn <br>　　相当于C语言中的#define macro=defn <br>　　 <br>-Umacro <br>　　相当于C语言中的#undef macro <br>-undef <br>　　取消对任何非标准宏的定义 <br>　　 <br>-Idir <br>　　在你是用#include"file"的时候,gcc/g++会先在当前目录查找你所制定的头文件,如 <br>果没有找到,他回到缺省的头文件目录找,如果使用-I制定了目录,他 <br>　　回先在你所制定的目录查找,然后再按常规的顺序去找. <br>　　对于#include&lt;file&gt;,gcc/g++会到-I制定的目录查找,查找不到,然后将到系统的缺 <br>省的头文件目录查找 <br>　　 <br>-I- <br>　　就是取消前一个参数的功能,所以一般在-Idir之后使用 <br>　　 <br>-idirafter dir <br>　　在-I的目录里面查找失败,讲到这个目录里面查找. <br>　　 <br>-iprefix prefix <br>-iwithprefix dir <br>　　一般一起使用,当-I的目录查找失败,会到prefix+dir下查找 <br>　　 <br>-nostdinc <br>　　使编译器不再系统缺省的头文件目录里面找头文件,一般和-I联合使用,明确限定头 <br>文件的位置 <br>　　 <br>-nostdin C++ <br>　　规定不在g++指定的标准路经中搜索,但仍在其他路径中搜索,.此选项在创libg++库 <br>使用 <br>　　 <br>-C <br>　　在预处理的时候,不删除注释信息,一般和-E使用,有时候分析程序，用这个很方便的 <br><br>　　 <br>-M <br>　　生成文件关联的信息。包含目标文件所依赖的所有源代码你可以用gcc -M hello.c <br>来测试一下，很简单。 <br>　　 <br>-MM <br>　　和上面的那个一样，但是它将忽略由#include&lt;file&gt;造成的依赖关系。 <br>　　 <br>-MD <br>　　和-M相同，但是输出将导入到.d的文件里面 <br>　　 <br>-MMD <br>　　和-MM相同，但是输出将导入到.d的文件里面 <br>　　 <br>-Wa,option <br>　　此选项传递option给汇编程序;如果option中间有逗号,就将option分成多个选项,然 <br>后传递给会汇编程序 <br>　　 <br>-Wl.option <br>　　此选项传递option给连接程序;如果option中间有逗号,就将option分成多个选项,然 <br>后传递给会连接程序. <br>　　 <br>-llibrary <br>　　制定编译的时候使用的库 <br>　　例子用法 <br>　　gcc -lcurses hello.c <br>　　使用ncurses库编译程序 <br>　　 <br>-Ldir <br>　　制定编译的时候，搜索库的路径。比如你自己的库，可以用它制定目录，不然 <br>　　编译器将只在标准库的目录找。这个dir就是目录的名称。 <br>　　 <br>-O0 <br>-O1 <br>-O2 <br>-O3 <br>　　编译器的优化选项的4个级别，-O0表示没有优化,-O1为缺省值，-O3优化级别最高　 <br>　 　　 <br>-g <br>　　只是编译器，在编译的时候，产生调试信息。 <br>　　 <br>-gstabs <br>　　此选项以stabs格式声称调试信息,但是不包括gdb调试信息. <br>　　 <br>-gstabs+ <br>　　此选项以stabs格式声称调试信息,并且包含仅供gdb使用的额外调试信息. <br>　　 <br>-ggdb <br>　　此选项将尽可能的生成gdb的可以使用的调试信息. <br>-static <br>　　此选项将禁止使用动态库，所以，编译出来的东西，一般都很大，也不需要什么 <br>动态连接库，就可以运行. <br>-share <br>　　此选项将尽量使用动态库，所以生成文件比较小，但是需要系统由动态库. <br>-traditional <br>　　试图让编译器支持传统的C语言特性 <br>[参考资料] <br>-Linux/UNIX高级编程 <br>　　中科红旗软件技术有限公司编著.清华大学出版社出版 <br>-Gcc man page <br>　　 <br>[ChangeLog] <br>-2002-08-10 <br>　　ver 0.1 发布最初的文档 <br>-2002-08-11 <br>　　ver 0.11 修改文档格式 <br>-2002-08-12 <br>　　ver 0.12 加入了对静态库，动态库的参数 <br>-2002-08-16 <br>　　ver 0.16 增加了gcc编译的4个阶段的命令 <br>运行 gcc/egcs <br>**********运行 gcc/egcs*********************** <br>　　GCC 是 GNU 的 C 和 C++ 编译器。实际上，GCC 能够编译三种语言：C、C++ 和 O <br>bject C（C 语言的一种面向对象扩展）。利用 gcc 命令可同时编译并连接 C 和 C++ <br>源程序。 <br>　　如果你有两个或少数几个 C 源文件，也可以方便地利用 GCC 编译、连接并生成可 <br>执行文件。例如，假设你有两个源文件 main.c 和 factorial.c 两个源文件，现在要编 <br>译生成一个计算阶乘的程序。 <br>代码: <br>----------------------- <br>清单 factorial.c <br>----------------------- <br>int factorial (int n) <br>{ <br>　　if (n &lt;= 1) <br>　　　return 1; <br>　　else <br>　　　return factorial (n - 1) * n; <br>} <br>----------------------- <br>清单 main.c <br>----------------------- <br>#include　&lt;stdio.h&gt; <br>#include　&lt;unistd.h&gt; <br>int factorial (int n); <br>int main (int argc, char **argv) <br>{ <br>　　int n; <br>　　if (argc &lt; 2) <br>　　{ <br>　　　　printf ("Usage: %s n\n", argv [0]); <br>　　　　return -1; <br>　　} <br>　　else <br>　　{ <br>　　　n = atoi (argv[1]); <br>　　　printf ("Factorial of %d is %d.\n", n, factorial (n)); <br>　　 } <br>　　return 0; <br>} <br>----------------------- <br>利用如下的命令可编译生成可执行文件，并执行程序： <br>$ gcc -o factorial main.c factorial.c <br>$ ./factorial 5 <br>Factorial of 5 is 120. <br>　　GCC 可同时用来编译 C 程序和 C++ 程序。一般来说，C 编译器通过源文件的后缀 <br>名来判断是 C 程序还是 C++ 程序。在 Linux 中，C 源文件的后缀名为 .c，而 C++ 源 <br>文件的后缀名为 .C 或 .cpp。但是，gcc 命令只能编译 C++ 源文件，而不能自动和 C <br>++ 程序使用的库连接。因此，通常使用 g++ 命令来完成 C++ 程序的编译和连接，该程 <br>序会自动调用 gcc 实现编译。假设我们有一个如下的 C++ 源文件（hello.C）： <br>#include &lt;iostream&gt; <br>void main (void) <br>{ <br>　　cout &lt;&lt; "Hello, world!" &lt;&lt; endl; <br>} <br>则可以如下调用 g++ 命令编译、连接并生成可执行文件： <br>$ g++ -o hello hello.C <br>$ ./hello <br>Hello, world! <br>**********************gcc/egcs 的主要选项********* <br>gcc 命令的常用选项 <br>选项 解释 <br>-ansi 只支持 ANSI 标准的 C 语法。这一选项将禁止 GNU C 的某些特色， <br>例如 asm 或 typeof 关键词。 <br>-c 只编译并生成目标文件。 <br>-DMACRO 以字符串“1”定义 MACRO 宏。 <br>-DMACRO=DEFN 以字符串“DEFN”定义 MACRO 宏。 <br>-E 只运行 C 预编译器。 <br>-g 生成调试信息。GNU 调试器可利用该信息。 <br>-IDIRECTORY 指定额外的头文件搜索路径DIRECTORY。 <br>-LDIRECTORY 指定额外的函数库搜索路径DIRECTORY。 <br>-lLIBRARY 连接时搜索指定的函数库LIBRARY。 <br>-m486 针对 486 进行代码优化。 <br>-o FILE 生成指定的输出文件。用在生成可执行文件时。 <br>-O0 不进行优化处理。 <br>-O 或 -O1 优化生成代码。 <br>-O2 进一步优化。 <br>-O3 比 -O2 更进一步优化，包括 inline 函数。 <br>-shared 生成共享目标文件。通常用在建立共享库时。 <br>-static 禁止使用共享连接。 <br>-UMACRO 取消对 MACRO 宏的定义。 <br>-w 不生成任何警告信息。 <br>-Wall 生成所有警告信息。</font><img src ="http://www.cppblog.com/apollo/aggbug/3720.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/apollo/" target="_blank">笑笑生</a> 2006-03-04 20:08 <a href="http://www.cppblog.com/apollo/articles/GCCComplate.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>GCC使用向导</title><link>http://www.cppblog.com/apollo/articles/GCCGuide.html</link><dc:creator>笑笑生</dc:creator><author>笑笑生</author><pubDate>Sat, 25 Feb 2006 04:29:00 GMT</pubDate><guid>http://www.cppblog.com/apollo/articles/GCCGuide.html</guid><wfw:comment>http://www.cppblog.com/apollo/comments/3493.html</wfw:comment><comments>http://www.cppblog.com/apollo/articles/GCCGuide.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/apollo/comments/commentRss/3493.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/apollo/services/trackbacks/3493.html</trackback:ping><description><![CDATA[<P>在为Linux开发应用程序时，绝大多数情况下使用的都是C语言，因此几乎每一位Linux程序员面临的首要问题都是如何灵活运用C编译器。目前 Linux下最常用的C语言编译器是GCC（GNU Compiler Collection），它是GNU项目中符合ANSI C标准的编译系统，能够编译用C、C++和Object C等语言编写的程序。GCC不仅功能非常强大，结构也异常灵活。最值得称道的一点就是它可以通过不同的前端模块来支持各种语言，如Java、 Fortran、Pascal、Modula-3和Ada等。 <BR><BR>开放、自由和灵活是Linux的魅力所在，而这一点在GCC上的体现就是程序员通过它能够更好地控制整个编译过程。在使用GCC编译程序时，编译过程可以被细分为四个阶段： <BR><BR>◆ 预处理（Pre-Processing） <BR><BR>◆ 编译（Compiling） <BR><BR>◆ 汇编（Assembling） <BR><BR>◆ 链接（Linking） <BR><BR>Linux 程序员可以根据自己的需要让GCC在编译的任何阶段结束，以便检查或使用编译器在该阶段的输出信息，或者对最后生成的二进制文件进行控制，以便通过加入不同数量和种类的调试代码来为今后的调试做好准备。和其它常用的编译器一样，GCC也提供了灵活而强大的代码优化功能，利用它可以生成执行效率更高的代码。 <BR><BR>GCC提供了30多条警告信息和三个警告级别，使用它们有助于增强程序的稳定性和可移植性。此外，GCC还对标准的C和C++语言进行了大量的扩展，提高程序的执行效率，有助于编译器进行代码优化，能够减轻编程的工作量。 <BR><BR>GCC起步 <BR><BR>在学习使用GCC之前，下面的这个例子能够帮助用户迅速理解GCC的工作原理，并将其立即运用到实际的项目开发中去。首先用熟悉的编辑器输入清单1所示的代码： <BR><BR>清单1：hello.c <BR><BR>#include &lt;stdio.h&gt;<BR>int main(void)<BR>{<BR>printf ("Hello world, Linux programming!n");<BR>return 0;<BR>}<BR><BR>然后执行下面的命令编译和运行这段程序： <BR><BR># gcc hello.c -o hello<BR># ./hello<BR>Hello world, Linux programming!<BR><BR>从程序员的角度看，只需简单地执行一条GCC命令就可以了，但从编译器的角度来看，却需要完成一系列非常繁杂的工作。首先，GCC需要调用预处理程序 cpp，由它负责展开在源文件中定义的宏，并向其中插入“#include”语句所包含的内容；接着，GCC会调用ccl和as将处理后的源代码编译成目标代码；最后，GCC会调用链接程序ld，把生成的目标代码链接成一个可执行程序。 <BR><BR>为了更好地理解GCC的工作过程，可以把上述编译过程分成几个步骤单独进行，并观察每步的运行结果。第一步是进行预编译，使用-E参数可以让GCC在预处理结束后停止编译过程： <BR><BR>#&nbsp;&nbsp;gcc -E hello.c -o hello.i<BR><BR>此时若查看hello.cpp文件中的内容，会发现stdio.h的内容确实都插到文件里去了，而其它应当被预处理的宏定义也都做了相应的处理。下一步是将hello.i编译为目标代码，这可以通过使用-c参数来完成： <BR><BR>#&nbsp;&nbsp;gcc -c hello.i -o hello.o<BR><BR>GCC默认将.i文件看成是预处理后的C语言源代码，因此上述命令将自动跳过预处理步骤而开始执行编译过程，也可以使用-x参数让GCC从指定的步骤开始编译。最后一步是将生成的目标文件链接成可执行文件： <BR><BR>#&nbsp;&nbsp;gcc hello.o -o hello<BR><BR>在采用模块化的设计思想进行软件开发时，通常整个程序是由多个源文件组成的，相应地也就形成了多个编译单元，使用GCC能够很好地管理这些编译单元。假设有一个由foo1.c和foo2.c两个源文件组成的程序，为了对它们进行编译，并最终生成可执行程序foo，可以使用下面这条命令： <BR><BR>#&nbsp;&nbsp;gcc foo1.c foo2.c -o foo<BR><BR>如果同时处理的文件不止一个，GCC仍然会按照预处理、编译和链接的过程依次进行。如果深究起来，上面这条命令大致相当于依次执行如下三条命令： <BR><BR># gcc -c foo1.c -o foo1.o<BR># gcc -c foo2.c -o foo2.o<BR># gcc foo1.o foo2.o -o foo<BR><BR>在编译一个包含许多源文件的工程时，若只用一条GCC命令来完成编译是非常浪费时间的。假设项目中有100个源文件需要编译，并且每个源文件中都包含 10000行代码，如果像上面那样仅用一条GCC命令来完成编译工作，那么GCC需要将每个源文件都重新编译一遍，然后再全部连接起来。很显然，这样浪费的时间相当多，尤其是当用户只是修改了其中某一个文件的时候，完全没有必要将每个文件都重新编译一遍，因为很多已经生成的目标文件是不会改变的。要解决这个问题，关键是要灵活运用GCC，同时还要借助像Make这样的工具。 <BR><BR>警告提示功能 <BR><BR>GCC包含完整的出错检查和警告提示功能，它们可以帮助Linux程序员写出更加专业和优美的代码。先来读读清单2所示的程序，这段代码写得很糟糕，仔细检查一下不难挑出很多毛病： <BR><BR>◆main函数的返回值被声明为void，但实际上应该是int； <BR><BR>◆使用了GNU语法扩展，即使用long long来声明64位整数，不符合ANSI/ISO C语言标准； <BR><BR>◆main函数在终止前没有调用return语句。 <BR><BR>清单2：illcode.c <BR><BR>#include &lt;stdio.h&gt;<BR>void main(void)<BR>{<BR>&nbsp;&nbsp;long long int var = 1;<BR>&nbsp;&nbsp;printf("It is not standard C code!n");<BR>}<BR><BR>下面来看看GCC是如何帮助程序员来发现这些错误的。当GCC在编译不符合ANSI/ISO C语言标准的源代码时，如果加上了-pedantic选项，那么使用了扩展语法的地方将产生相应的警告信息： <BR><BR># gcc -pedantic illcode.c -o illcode<BR>illcode.c: In function `main':<BR>illcode.c:9: ISO C89 does not support `long long'<BR>illcode.c:8: return type of `main' is not `int'<BR><BR>需要注意的是，-pedantic编译选项并不能保证被编译程序与ANSI/ISO C标准的完全兼容，它仅仅只能用来帮助Linux程序员离这个目标越来越近。或者换句话说，-pedantic选项能够帮助程序员发现一些不符合 ANSI/ISO C标准的代码，但不是全部，事实上只有ANSI/ISO C语言标准中要求进行编译器诊断的那些情况，才有可能被GCC发现并提出警告。 <BR><BR>除了-pedantic之外，GCC还有一些其它编译选项也能够产生有用的警告信息。这些选项大多以-W开头，其中最有价值的当数-Wall了，使用它能够使GCC产生尽可能多的警告信息： <BR><BR># gcc -Wall illcode.c -o illcode<BR>illcode.c:8: warning: return type of `main' is not `int'<BR>illcode.c: In function `main':<BR>illcode.c:9: warning: unused variable `var'<BR><BR>GCC给出的警告信息虽然从严格意义上说不能算作是错误，但却很可能成为错误的栖身之所。一个优秀的Linux程序员应该尽量避免产生警告信息，使自己的代码始终保持简洁、优美和健壮的特性。 <BR><BR>在处理警告方面，另一个常用的编译选项是-Werror，它要求GCC将所有的警告当成错误进行处理，这在使用自动编译工具（如Make等）时非常有用。如果编译时带上-Werror选项，那么GCC会在所有产生警告的地方停止编译，迫使程序员对自己的代码进行修改。只有当相应的警告信息消除时，才可能将编译过程继续朝前推进。执行情况如下： <BR><BR># gcc -Wall -Werror illcode.c -o illcode<BR>cc1: warnings being treated as errors<BR>illcode.c:8: warning: return type of `main' is not `int'<BR>illcode.c: In function `main':<BR>illcode.c:9: warning: unused variable `var'<BR><BR>对Linux程序员来讲，GCC给出的警告信息是很有价值的，它们不仅可以帮助程序员写出更加健壮的程序，而且还是跟踪和调试程序的有力工具。建议在用GCC编译源代码时始终带上-Wall选项，并把它逐渐培养成为一种习惯，这对找出常见的隐式编程错误很有帮助。 <BR><BR>在Linux 下开发软件时，完全不使用第三方函数库的情况是比较少见的，通常来讲都需要借助一个或多个函数库的支持才能够完成相应的功能。从程序员的角度看，函数库实际上就是一些头文件（.h）和库文件（.so或者.a）的集合。虽然Linux下的大多数函数都默认将头文件放到/usr/include/目录下，而库文件则放到/usr/lib/目录下，但并不是所有的情况都是这样。正因如此，GCC在编译时必须有自己的办法来查找所需要的头文件和库文件。 <BR><BR>GCC采用搜索目录的办法来查找所需要的文件，-I选项可以向GCC的头文件搜索路径中添加新的目录。例如，如果在/home/xiaowp/include/目录下有编译时所需要的头文件，为了让GCC能够顺利地找到它们，就可以使用-I选项： <BR><BR># gcc foo.c -I /home/xiaowp/include -o foo<BR><BR>同样，如果使用了不在标准位置的库文件，那么可以通过-L选项向GCC的库文件搜索路径中添加新的目录。例如，如果在/home/xiaowp/lib/目录下有链接时所需要的库文件libfoo.so，为了让GCC能够顺利地找到它，可以使用下面的命令： <BR><BR># gcc foo.c -L /home/xiaowp/lib -lfoo -o foo<BR><BR>值得好好解释一下的是-l选项，它指示GCC去连接库文件libfoo.so。Linux下的库文件在命名时有一个约定，那就是应该以lib三个字母开头，由于所有的库文件都遵循了同样的规范，因此在用-l选项指定链接的库文件名时可以省去lib三个字母，也就是说GCC在对-lfoo进行处理时，会自动去链接名为libfoo.so的文件。 <BR><BR>Linux下的库文件分为两大类分别是动态链接库（通常以.so结尾）和静态链接库（通常以.a 结尾），两者的差别仅在程序执行时所需的代码是在运行时动态加载的，还是在编译时静态加载的。默认情况下，GCC在链接时优先使用动态链接库，只有当动态链接库不存在时才考虑使用静态链接库，如果需要的话可以在编译时加上-static选项，强制使用静态链接库。例如，如果在 /home/xiaowp/lib/目录下有链接时所需要的库文件libfoo.so和libfoo.a，为了让GCC在链接时只用到静态链接库，可以使用下面的命令： <BR><BR># gcc foo.c -L /home/xiaowp/lib -static -lfoo -o foo<BR>代码优化 <BR><BR>代码优化指的是编译器通过分析源代码，找出其中尚未达到最优的部分，然后对其重新进行组合，目的是改善程序的执行性能。GCC提供的代码优化功能非常强大，它通过编译选项-On来控制优化代码的生成，其中n是一个代表优化级别的整数。对于不同版本的GCC来讲，n的取值范围及其对应的优化效果可能并不完全相同，比较典型的范围是从0变化到2或3。 <BR><BR>编译时使用选项-O可以告诉GCC同时减小代码的长度和执行时间，其效果等价于-O1。在这一级别上能够进行的优化类型虽然取决于目标处理器，但一般都会包括线程跳转（Thread Jump）和延迟退栈（Deferred Stack Pops）两种优化。选项-O2告诉GCC除了完成所有-O1级别的优化之外，同时还要进行一些额外的调整工作，如处理器指令调度等。选项-O3则除了完成所有-O2级别的优化之外，还包括循环展开和其它一些与处理器特性相关的优化工作。通常来说，数字越大优化的等级越高，同时也就意味着程序的运行速度越快。许多Linux程序员都喜欢使用-O2选项，因为它在优化长度、编译时间和代码大小之间，取得了一个比较理想的平衡点。 <BR><BR>下面通过具体实例来感受一下GCC的代码优化功能，所用程序如清单3所示。 <BR><BR>清单3：optimize.c <BR><BR>#include &lt;stdio.h&gt; </P>
<P>int main(void)</P>
<P>{&nbsp;</P>
<P>&nbsp;double counter;&nbsp;&nbsp;</P>
<P>double result;&nbsp;&nbsp;</P>
<P>double temp;&nbsp;&nbsp;</P>
<P>for (counter = 0;&nbsp; &nbsp; counter &lt; 2000.0 * 2000.0 * 2000.0&nbsp;&nbsp;/ 20.0 + 2020;&nbsp; &nbsp; counter += (5 - 1) / 4) </P>
<P>{&nbsp; &nbsp; temp = counter / 1979;&nbsp; &nbsp; result&nbsp;&nbsp;= counter;&nbsp; &nbsp;&nbsp; &nbsp;}&nbsp;</P>
<P>&nbsp;printf("Result is %lfn", result);&nbsp;</P>
<P>&nbsp;return 0;</P>
<P>}<BR><BR>首先不加任何优化选项进行编译： <BR><BR># gcc -Wall optimize.c -o optimize<BR><BR>借助Linux提供的time命令，可以大致统计出该程序在运行时所需要的时间： <BR><BR># time ./optimizeResult is 400002019.000000real&nbsp; &nbsp; 0m14.942suser&nbsp; &nbsp; 0m14.940ssys&nbsp; &nbsp;&nbsp;&nbsp;0m0.000s<BR><BR>接下去使用优化选项来对代码进行优化处理： <BR><BR># gcc -Wall -O optimize.c -o optimize<BR><BR>在同样的条件下再次测试一下运行时间： <BR><BR># time ./optimizeResult is 400002019.000000real&nbsp; &nbsp; 0m3.256suser&nbsp; &nbsp; 0m3.240ssys&nbsp; &nbsp;&nbsp;&nbsp;0m0.000s<BR><BR>对比两次执行的输出结果不难看出，程序的性能的确得到了很大幅度的改善，由原来的14秒缩短到了3秒。这个例子是专门针对GCC的优化功能而设计的，因此优化前后程序的执行速度发生了很大的改变。尽管GCC的代码优化功能非常强大，但作为一名优秀的Linux程序员，首先还是要力求能够手工编写出高质量的代码。如果编写的代码简短，并且逻辑性强，编译器就不会做更多的工作，甚至根本用不着优化。 <BR></P>
<P>&nbsp;优化虽然能够给程序带来更好的执行性能，但在如下一些场合中应该避免优化代码： <BR><BR>◆ 程序开发的时候 优化等级越高，消耗在编译上的时间就越长，因此在开发的时候最好不要使用优化选项，只有到软件发行或开发结束的时候，才考虑对最终生成的代码进行优化。 <BR><BR>◆ 资源受限的时候 一些优化选项会增加可执行代码的体积，如果程序在运行时能够申请到的内存资源非常紧张（如一些实时嵌入式设备），那就不要对代码进行优化，因为由这带来的负面影响可能会产生非常严重的后果。 <BR><BR>◆ 跟踪调试的时候 在对代码进行优化的时候，某些代码可能会被删除或改写，或者为了取得更佳的性能而进行重组，从而使跟踪和调试变得异常困难。 <BR><BR>调试 <BR><BR>一个功能强大的调试器不仅为程序员提供了跟踪程序执行的手段，而且还可以帮助程序员找到解决问题的方法。对于Linux程序员来讲，GDB（GNU Debugger）通过与GCC的配合使用，为基于Linux的软件开发提供了一个完善的调试环境。 <BR><BR>默认情况下，GCC在编译时不会将调试符号插入到生成的二进制代码中，因为这样会增加可执行文件的大小。如果需要在编译时生成调试符号信息，可以使用GCC 的-g或者-ggdb选项。GCC在产生调试符号时，同样采用了分级的思路，开发人员可以通过在-g选项后附加数字1、2或3来指定在代码中加入调试信息的多少。默认的级别是2（-g2），此时产生的调试信息包括扩展的符号表、行号、局部或外部变量信息。级别3（-g3）包含级别2中的所有调试信息，以及源代码中定义的宏。级别1（-g1）不包含局部变量和与行号有关的调试信息，因此只能够用于回溯跟踪和堆栈转储之用。回溯跟踪指的是监视程序在运行过程中的函数调用历史，堆栈转储则是一种以原始的十六进制格式保存程序执行环境的方法，两者都是经常用到的调试手段。 <BR><BR>GCC产生的调试符号具有普遍的适应性，可以被许多调试器加以利用，但如果使用的是GDB，那么还可以通过-ggdb选项在生成的二进制代码中包含GDB专用的调试信息。这种做法的优点是可以方便GDB的调试工作，但缺点是可能导致其它调试器（如DBX）无法进行正常的调试。选项-ggdb能够接受的调试级别和-g是完全一样的，它们对输出的调试符号有着相同的影响。 <BR><BR>需要注意的是，使用任何一个调试选项都会使最终生成的二进制文件的大小急剧增加，同时增加程序在执行时的开销，因此调试选项通常仅在软件的开发和调试阶段使用。调试选项对生成代码大小的影响从下面的对比过程中可以看出来： <BR><BR># gcc optimize.c -o optimize</P>
<P># ls optimize -l-rwxrwxr-x&nbsp;&nbsp;1 xiaowp&nbsp; &nbsp;xiaowp&nbsp;&nbsp;11649 Nov 20 08:53 optimize&nbsp;&nbsp;(未加调试选项)</P>
<P># gcc -g optimize.c -o optimize</P>
<P># ls optimize -l-rwxrwxr-x&nbsp;&nbsp;1 xiaowp&nbsp; &nbsp;xiaowp&nbsp;&nbsp;15889 Nov 20 08:54 optimize&nbsp;&nbsp;(加入调试选项)<BR><BR>虽然调试选项会增加文件的大小，但事实上Linux中的许多软件在测试版本甚至最终发行版本中仍然使用了调试选项来进行编译，这样做的目的是鼓励用户在发现问题时自己动手解决，是Linux的一个显著特色。 <BR><BR>下面还是通过一个具体的实例说明如何利用调试符号来分析错误，所用程序见清单4所示。 <BR><BR>清单4：crash.c <BR><BR>#include &lt;stdio.h&gt;</P>
<P>&nbsp;int main(void)</P>
<P>{&nbsp;&nbsp;</P>
<P>int input =0;&nbsp;&nbsp;</P>
<P>printf("Input an integer:");&nbsp;&nbsp;</P>
<P>scanf("%d", input);&nbsp;&nbsp;</P>
<P>printf("The integer you input is %dn", input);&nbsp;</P>
<P>&nbsp;return 0;</P>
<P>}<BR><BR>编译并运行上述代码，会产生一个严重的段错误（Segmentation fault）如下： <BR><BR># gcc -g crash.c -o crash</P>
<P># ./crashInput </P>
<P>an integer:10Segmentation fault<BR><BR>为了更快速地发现错误所在，可以使用GDB进行跟踪调试，方法如下： <BR><BR># gdb crashGNU gdb Red Hat Linux (5.3post-0.20021129.18rh)……(gdb)<BR><BR>当GDB提示符出现的时候，表明GDB已经做好准备进行调试了，现在可以通过run命令让程序开始在GDB的监控下运行： <BR><BR>(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<BR><BR>仔细分析一下GDB给出的输出结果不难看出，程序是由于段错误而导致异常中止的，说明内存操作出了问题，具体发生问题的地方是在调用 _IO_vfscanf_internal ( )的时候。为了得到更加有价值的信息，可以使用GDB提供的回溯跟踪命令backtrace，执行结果如下： <BR><BR>(gdb) backtrace</P>
<P>#0&nbsp;&nbsp;0x4008576b in _IO_vfscanf_internal () from /lib/libc.so.6</P>
<P>#1&nbsp;&nbsp;0xbffff0c0 in ?? ()</P>
<P>#2&nbsp;&nbsp;0x4008e0ba in scanf () from /lib/libc.so.6</P>
<P>#3&nbsp;&nbsp;0x08048393 in main () at crash.c:11</P>
<P>#4&nbsp;&nbsp;0x40042917 in __libc_start_main () from /lib/libc.so.6<BR><BR>跳过输出结果中的前面三行，从输出结果的第四行中不难看出，GDB已经将错误定位到crash.c中的第11行了。现在仔细检查一下： <BR><BR>(gdb) frame 3</P>
<P>#3&nbsp;&nbsp;0x08048393 in main () at crash.c:1111&nbsp; &nbsp;&nbsp; &nbsp; scanf("%d", input);<BR><BR>使用GDB提供的frame命令可以定位到发生错误的代码段，该命令后面跟着的数值可以在backtrace命令输出结果中的行首找到。现在已经发现错误所在了，应该将 <BR><BR>scanf("%d", input);改为scanf("%d", &amp;input);<BR><BR>完成后就可以退出GDB了，命令如下： <BR><BR>(gdb) quit<BR><BR>GDB的功能远远不止如此，它还可以单步跟踪程序、检查内存变量和设置断点等。 <BR><BR>调试时可能会需要用到编译器产生的中间结果，这时可以使用-save-temps选项，让GCC将预处理代码、汇编代码和目标代码都作为文件保存起来。如果想检查生成的代码是否能够通过手工调整的办法来提高执行性能，在编译过程中生成的中间文件将会很有帮助，具体情况如下： <BR><BR># gcc -save-temps foo.c -o foo</P>
<P># ls foo*foo&nbsp;&nbsp;foo.c&nbsp;&nbsp;foo.i&nbsp;&nbsp;foo.s<BR><BR>GCC 支持的其它调试选项还包括-p和-pg，它们会将剖析（Profiling）信息加入到最终生成的二进制代码中。剖析信息对于找出程序的性能瓶颈很有帮助，是协助Linux程序员开发出高性能程序的有力工具。在编译时加入-p选项会在生成的代码中加入通用剖析工具（Prof）能够识别的统计信息，而- pg选项则生成只有GNU剖析工具（Gprof）才能识别的统计信息。 <BR><BR>最后提醒一点，虽然GCC允许在优化的同时加入调试符号信息，但优化后的代码对于调试本身而言将是一个很大的挑战。代码在经过优化之后，在源程序中声明和使用的变量很可能不再使用，控制流也可能会突然跳转到意外的地方，循环语句有可能因为循环展开而变得到处都有，所有这些对调试来讲都将是一场噩梦。建议在调试的时候最好不使用任何优化选项，只有当程序在最终发行的时候才考虑对其进行优化。 <BR>上次的培训园地中介绍了GCC的编译过程、警告提示功能、库依赖、代码优化和程序调试六个方面的内容。这期是最后的一部分内容。 <BR>在将源代码变成可执行文件的过程中，需要经过许多中间步骤，包含预处理、编译、汇编和连接。这些过程实际上是由不同的程序负责完成的。大多数情况下GCC可以为Linux程序员完成所有的后台工作，自动调用相应程序进行处理。 <BR><BR>这样做有一个很明显的缺点，就是GCC在处理每一个源文件时，最终都需要生成好几个临时文件才能完成相应的工作，从而无形中导致处理速度变慢。例如，GCC 在处理一个源文件时，可能需要一个临时文件来保存预处理的输出、一个临时文件来保存编译器的输出、一个临时文件来保存汇编器的输出，而读写这些临时文件显然需要耗费一定的时间。当软件项目变得非常庞大的时候，花费在这上面的代价可能会变得很沉重。 <BR><BR>解决的办法是，使用Linux提供的一种更加高效的通信方式—管道。它可以用来同时连接两个程序，其中一个程序的输出将被直接作为另一个程序的输入，这样就可以避免使用临时文件，但编译时却需要消耗更多的内存。 <BR><BR>在编译过程中使用管道是由GCC的-pipe选项决定的。下面的这条命令就是借助GCC的管道功能来提高编译速度的： <BR><BR># gcc -pipe foo.c -o foo<BR><BR>在编译小型工程时使用管道，编译时间上的差异可能还不是很明显，但在源代码非常多的大型工程中，差异将变得非常明显。 <BR><BR>文件扩展名 <BR><BR>在使用GCC的过程中，用户对一些常用的扩展名一定要熟悉，并知道其含义。为了方便大家学习使用GCC，在此将这些扩展名罗列如下： <BR><BR>.c C原始程序； <BR><BR>.C C++原始程序； <BR><BR>.cc C++原始程序； <BR><BR>.cxx C++原始程序； <BR><BR>.m Objective-C原始程序； <BR><BR>.i 已经过预处理的C原始程序； <BR><BR>.ii 已经过预处理之C++原始程序； <BR><BR>.s 组合语言原始程序； <BR><BR>.S 组合语言原始程序； <BR><BR>.h 预处理文件(标头文件)； <BR><BR>.o 目标文件； <BR><BR>.a 存档文件。 <BR><BR>GCC常用选项 <BR><BR>GCC作为Linux下C/C++重要的编译环境，功能强大，编译选项繁多。为了方便大家日后编译方便，在此将常用的选项及说明罗列出来如下： <BR><BR>-c 通知GCC取消链接步骤，即编译源码并在最后生成目标文件； <BR><BR>-Dmacro 定义指定的宏，使它能够通过源码中的#ifdef进行检验； <BR><BR>-E 不经过编译预处理程序的输出而输送至标准输出； <BR><BR>-g3 获得有关调试程序的详细信息，它不能与-o选项联合使用； <BR><BR>-Idirectory 在包含文件搜索路径的起点处添加指定目录； <BR><BR>-llibrary 提示链接程序在创建最终可执行文件时包含指定的库； <BR><BR>-O、-O2、-O3 将优化状态打开，该选项不能与-g选项联合使用； <BR><BR>-S 要求编译程序生成来自源代码的汇编程序输出； <BR><BR>-v 启动所有警报； <BR><BR>-Wall 在发生警报时取消编译操作，即将警报看作是错误； <BR><BR>-Werror 在发生警报时取消编译操作，即把报警当作是错误； <BR><BR>-w 禁止所有的报警。 <BR><BR>小结 <BR><BR>GCC 是在Linux下开发程序时必须掌握的工具之一。本文对GCC做了一个简要的介绍，主要讲述了如何使用GCC编译程序、产生警告信息、调试程序和加快 GCC的编译速度。对所有希望早日跨入Linux开发者行列的人来说，GCC就是成为一名优秀的Linux程序员的起跑线。 </P><BR><img src ="http://www.cppblog.com/apollo/aggbug/3493.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/apollo/" target="_blank">笑笑生</a> 2006-02-25 12:29 <a href="http://www.cppblog.com/apollo/articles/GCCGuide.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>VC小技巧15个 </title><link>http://www.cppblog.com/apollo/articles/3461.html</link><dc:creator>笑笑生</dc:creator><author>笑笑生</author><pubDate>Fri, 24 Feb 2006 05:26:00 GMT</pubDate><guid>http://www.cppblog.com/apollo/articles/3461.html</guid><wfw:comment>http://www.cppblog.com/apollo/comments/3461.html</wfw:comment><comments>http://www.cppblog.com/apollo/articles/3461.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/apollo/comments/commentRss/3461.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/apollo/services/trackbacks/3461.html</trackback:ping><description><![CDATA[<P><STRONG>一、&nbsp;一次只运行一个程序实例</STRONG><BR>下列两种方式都可以实现，建议采用第二种方式：<BR>1、&nbsp;if( FindWindow(NULL,"程序标题")) <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exit(0);<BR>2、BOOL CDemoTBarEApp::InstanceIsRun()<BR>{<BR>&nbsp;HANDLE m_hMutex;<BR>&nbsp;m_hMutex = ::CreateMutex(NULL, TRUE, _T("YourApplication"));<BR>&nbsp;ASSERT(m_hMutex);<BR>&nbsp;if (GetLastError() == ERROR_ALREADY_EXISTS)<BR>&nbsp;{<BR>&nbsp;&nbsp;m_hMutex = NULL;<BR>&nbsp;&nbsp;return TRUE;//实例已经运行<BR>&nbsp;}<BR>&nbsp;return FALSE;//实例未运行<BR>}</P>
<P><STRONG>二、&nbsp;装载光标</STRONG><BR>SetCursor(AfxGetApp()-&gt;LoadStandardCursor(IDC_WAIT));<BR>　　其中::SetCursor()是全局函数，用来设置整个例程的光标参数是宏定义光标句柄。AfxGetApp ()是一个系统函数，它返回当前的一个CWinApp对象。其成员函数LoadStandardCursor()用来读取一个系统指针，每一种系统指针的具体宏定义如下：<BR>IDC_APPSTARTING&nbsp; 带小沙漏的标准箭头<BR>IDC_ARROW&nbsp; 标准箭头<BR>IDC_CROSS&nbsp; 十字光标（用于定位）<BR>IDC_HAND&nbsp; Windows 2000：手型<BR>IDC_HELP&nbsp; 带问号的箭头<BR>IDC_IBEAM&nbsp; I型标<BR>IDC_ICON&nbsp; Obsolete for applications marked version 4.0 or later. <BR>IDC_NO&nbsp;&nbsp; 禁止符号<BR>IDC_SIZE&nbsp; Obsolete for applications marked version 4.0 or later. Use IDC_SIZEALL. <BR>IDC_SIZEALL&nbsp; 十字箭头<BR>IDC_SIZENESW&nbsp; 指向东北和西南的双向箭头<BR>IDC_SIZENS&nbsp; 指向南和北的双向箭头<BR>IDC_SIZENWSE&nbsp; 指向西北和东南的双向箭头<BR>IDC_SIZEWE&nbsp; 指向东西的双向箭头<BR>IDC_UPARROW&nbsp; 上箭头<BR>IDC_WAIT&nbsp; 沙漏</P>
<P><STRONG>三、获得主框架：</STRONG>&nbsp;<BR>CMainFrame * pMainframe = (CMainFrame *) AfxGetApp()-&gt;m_pMainWnd;<BR>.获取应用程序的实例句柄：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Example: HANDLE hInstance=AfxGetInstanceHandle();</P>
<P>获得应用程序主窗口的指针：<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Example: AfxGetMainWnd() -&gt;ShowWindow(SW_SHOWMAXMIZED); //使程序最大化</P>
<P><STRONG>四、重新建立字体的代码</STRONG><BR>&nbsp;if(m_fontLogo.m_hObject)<BR>&nbsp;&nbsp;m_fontLogo.Detach();</P>
<P>&nbsp;m_fontLogo.CreateFont(nHeight, 0, 0, 0, nWeight, bItalic, bUnderline,0,0,0,0,0,0, Name);</P>
<P><STRONG>五、用指定颜色填充区域</STRONG><BR>&nbsp;dc.FillSolidRect(rect, ::GetSysColor(COLOR_3DFACE));</P>
<P><STRONG>六、绘制立体字体效果的字体，很值得一看<BR></STRONG>void CTestView::OnPaint() <BR>{<BR>&nbsp;CPaintDC dc(this); // device context for painting<BR>&nbsp;<BR>&nbsp;CRect rect;<BR>&nbsp;GetWindowRect(rect);</P>
<P>&nbsp;CFont &nbsp;m_fontLogo;<BR>&nbsp;m_fontLogo.CreateFont(24, 0, 0, 0, FW_BOLD, true,<BR>&nbsp;&nbsp;FALSE,0,0,0,0,0,0, "Arial");<BR>&nbsp;CString m_LogoText;<BR>&nbsp;m_LogoText=_T("Benlux Pro3D System");<BR>&nbsp;dc.SetBkMode(TRANSPARENT);</P>
<P>&nbsp;CFont * OldFont = dc.SelectObject(&amp;m_fontLogo);</P>
<P>&nbsp;// draw text in DC<BR>&nbsp;COLORREF OldColor = dc.SetTextColor( ::GetSysColor( COLOR_3DHILIGHT));</P>
<P>&nbsp;rect.right = rect.Width();<BR>&nbsp;rect.bottom = rect.Height();<BR>&nbsp;rect.left = rect.top = 0;<BR>&nbsp;dc.FillSolidRect(rect, ::GetSysColor(COLOR_3DFACE));</P>
<P><BR>&nbsp;dc.DrawText( m_LogoText, rect + CPoint(1,1), DT_SINGLELINE | DT_LEFT | DT_VCENTER);<BR>&nbsp;dc.SetTextColor( ::GetSysColor( COLOR_3DSHADOW));<BR>&nbsp;dc.DrawText( m_LogoText, rect, DT_SINGLELINE | DT_LEFT | DT_VCENTER);</P>
<P>&nbsp;// restore old text color<BR>&nbsp;dc.SetTextColor( OldColor);<BR>&nbsp;// restore old font<BR>&nbsp;dc.SelectObject(OldFont);&nbsp;<BR>&nbsp;// Do not call CView::OnPaint() for painting messages<BR>}</P>
<P><STRONG>七、简单的消息检索和抽取函数，能够让系统响应其它操作<BR></STRONG>BOOL PeekAndPump()<BR>{<BR>&nbsp;static MSG msg;</P>
<P>&nbsp;while (::PeekMessage(&amp;msg,NULL,0,0,PM_NOREMOVE)) {<BR>&nbsp;&nbsp;if (!AfxGetApp()-&gt;PumpMessage()) {<BR>&nbsp;&nbsp;&nbsp;::PostQuitMessage(0);<BR>&nbsp;&nbsp;&nbsp;return FALSE;<BR>&nbsp;&nbsp;}&nbsp;<BR>&nbsp;}<BR>&nbsp;return TRUE;<BR>}</P>
<P><STRONG>八、在你的程序中用动画光标替换默认的等待光标 (ANI光标的使用)</STRONG><BR>&nbsp;HCURSOR m_hAniCursor=NULL;<BR>&nbsp;BeginWaitCursor();&nbsp;&nbsp; //begin wait cursor for api function<BR>&nbsp;<BR>&nbsp;//load ani cursor from file in current path<BR>&nbsp;TCHAR cursorPath[MAX_PATH];&nbsp;GetModuleFileName(NULL,cursorPath,MAX_PATH);<BR>&nbsp;char drive[_MAX_DRIVE];<BR>&nbsp;char dir[_MAX_DIR];<BR>&nbsp;char fname[_MAX_FNAME];<BR>&nbsp;char ext[_MAX_EXT];<BR>&nbsp;_splitpath(cursorPath, drive, dir, fname, ext );<BR>&nbsp;sprintf(cursorPath,"%s%swait.ani",drive,dir);&nbsp; //ani cursor file name is wait.ani<BR>&nbsp;<BR>&nbsp;m_hAniCursor= LoadCursorFromFile(cursorPath);<BR>&nbsp;HCURSOR oldCursor;<BR>&nbsp;if(m_hAniCursor != NULL)<BR>&nbsp;&nbsp;oldCursor=SetCursor(m_hAniCursor);<BR>&nbsp;<BR>&nbsp;for(long i=0;i&lt;1000;i++)&nbsp; <BR>&nbsp;&nbsp;Sleep(5);<BR>&nbsp;<BR>&nbsp;oldCursor=NULL;<BR>&nbsp;m_hAniCursor=NULL;<BR>&nbsp;EndWaitCursor(); &nbsp;//end wait cursor for api function</P>
<P><STRONG>九、如何限制编辑框中的准许字符</STRONG><BR>&nbsp;&nbsp;&nbsp;&nbsp; 如果用户在编辑控件中只允许接收数字，可以使用一个标准的编辑控件并指<BR>定新的创建标志ES_NUMBERS,它是Windows 95新增加的标志，该标志限制 编辑控<BR>件只按收数字字符。<BR>如果用户需要复杂的编辑控件，可以使用Microsoft 的屏蔽编辑控件，它是一个很有用的OLE定制控件。<BR>&nbsp;&nbsp;&nbsp; 如果希望不使用OLE 定制控件自己处理字符，可以派生一个CEdit 类并处理WM_CHAR消息，然后从编辑控件中过滤出特定的字符。首先，使用ClassWizard 建立一个 CEdit的派生类，其次，在对话类中指定一个成员变量将编辑控件分类在OnInitdialog 中调用CWnd: : SubclassDlgItem .</P>
<P>//In your dialog class declaration (.H file )<BR>private :<BR>&nbsp;&nbsp;&nbsp; CMyEdit m_wndEdit ; // Instance of your new edit control .</P>
<P>//In you dialog class implementation (.CPP file )<BR>BOOL CSampleDialog : : OnInitDialog ( )<BR>{</P>
<P>&nbsp;&nbsp;&nbsp; //Subclass the edit lontrod .<BR>&nbsp;&nbsp;&nbsp; m_wndEdit .SubclassDlgItem&nbsp; (IDC_EDIT,this );<BR>&nbsp;&nbsp;&nbsp; …<BR>}<BR>&nbsp;&nbsp;&nbsp; 使用ClassWizard处理WM_CHAR消息，计算nChar参量并决定所执行的操作，用户可以确定是否修改、传送字符。下例说明了如何显示字母字符，如果字符是字母字符，则调用CWnd ; OnChar，否则不调用OnChar.<BR>//Only display alphabetic dharacters .<BR>void CMyEdit : : OnChar (UINT nChar , UINT nRepCnt , UITN nFlags )<BR>{<BR>&nbsp;&nbsp;&nbsp; //Determine if nChar is an alphabetic character .<BR>&nbsp;&nbsp;&nbsp; if (: : IsCharAlpha&nbsp; ( ( TCHAR) nChar ) )<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CEdit : : OnChar (nChar, nRepCnt , nFlags );<BR>}<BR>&nbsp;&nbsp;&nbsp; 如果要修改字符，则不能仅仅简单地用修改过的nChar调用CEdit : : OnChar。要修改一个字符，需要首先修改nChar，然后用修改过的nChar调用CWnd: : DefWindowProc。下例说明了如何将字符转变为大写：<BR>//Make all characters uppercase<BR>void CMyEdit : : OnChar (UINT nChar , UINT nRepCnt , UINT nFlags )<BR>{<BR>&nbsp;&nbsp;&nbsp; //Make sure character is uppercase .<BR>&nbsp;&nbsp;&nbsp; if (: : IsCharAlpha&nbsp; ( .( TCHAR) nChar)<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nChar=: : CharUpper (nChar ) ;<BR>&nbsp;&nbsp;&nbsp; //Bypass default OnChar processing and directly call&nbsp; default window proc.<BR>&nbsp;&nbsp;&nbsp; DefWindProc (WM_CHAR, nChar , MAKELPARAM (nRepCnt , nFlags )) ;<BR>}</P>
<P><STRONG>十、串太长时如何在其末尾显示一个省略号</STRONG><BR>&nbsp;&nbsp;&nbsp; 调用CDC:: DrawText并指定DT_END_ELLIPSIS标志，这样就可以用小略号取代串末尾的字符使其适合于指定的边界矩形。如果要显示路径信息，指定DT_END_ELLIPSIS标志并省略号取代串中间的字符。<BR>void CSampleView:: OnDraw (CDC* pDC)<BR>{<BR>&nbsp;&nbsp;&nbsp;&nbsp; CTestDoc* pDoc=GetDocument ();<BR>&nbsp;&nbsp;&nbsp;&nbsp; ASSERT_VALID (pDoc);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp; //Add ellpsis to end of string if it does not fit<BR>&nbsp;&nbsp;&nbsp;&nbsp; pDC-&gt;Drawtext (CString ("This is a long string"),<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CRect (10, 10, 80, 30), DT_LEFT | DT_END_ELLIPSIS);</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp; //Add ellpsis to middle of string if it does not fit<BR>&nbsp;&nbsp;&nbsp;&nbsp; pDC-&gt;DrawText (AfxgetApp () -&gt;m_pszhelpfilePath,<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CRect (10, 40, 200, 60), DT_LEFT | DT_PATH_ELLIPSIS);<BR>}</P>
<P><STRONG>十一、如何实现一个橡皮区矩形(具有踪迹矩形并可移动、缩放的矩形)<BR></STRONG>&nbsp;&nbsp;&nbsp;&nbsp; CRectTracker是一个很有用的类，可以通过调用CRectTracker:: TrackRubberBand响应WM_LBUTTONDOWN消息来创建一个橡皮区矩形。下例表明使用CRectTracker移动和重置视窗中的蓝色椭圆的大小是很容易的事情。<BR>&nbsp;&nbsp;&nbsp; 首先，在文档类中声明一个CRectTracker数据成员：<BR>class CTestDoc: Public CDocument<BR>{…<BR>public:<BR>&nbsp;CRectTracker m_tracker;<BR>…<BR>};<BR>&nbsp;&nbsp;&nbsp;&nbsp; 其次，在文档类的构造函数中初始化CRectTracker 对象：<BR>CTestDoc::CTestDoc()<BR>{<BR>&nbsp;m_tracker.m_rect.SetRect (10, 10, 300, 300);<BR>&nbsp;m_tracker.m_nStyle=CRectTracker:: resizeInside&nbsp; |<BR>&nbsp;&nbsp;CRectTracker:: dottedLine;&nbsp;<BR>}<BR>&nbsp;&nbsp;&nbsp;&nbsp; 然后，在视图类的OnDraw函数中画椭圆和踪迹矩形：<BR>void CTestView::OnDraw(CDC* pDC)<BR>{<BR>&nbsp;CTestDoc* pDoc = GetDocument();<BR>&nbsp;ASSERT_VALID(pDoc);</P>
<P>//Select blue brush into device context.<BR>&nbsp;&nbsp;&nbsp;&nbsp; CBrush brush (RGB (0, 0, 255));<BR>&nbsp;&nbsp;&nbsp;&nbsp; CBrush* pOldBrush=pDC-&gt;SelectObject (&amp;brush);</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp; //draw ellipse in tracking rectangle.<BR>&nbsp;&nbsp;&nbsp;&nbsp; CRect rcEllipse;<BR>&nbsp;&nbsp;&nbsp;&nbsp; pDoc-&gt;m_tracker.GetTrueRect (rcEllipse);<BR>&nbsp;&nbsp;&nbsp;&nbsp; pDC-&gt;Ellipse (rcEllipse);</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp; //Draw tracking rectangle.<BR>&nbsp;&nbsp;&nbsp;&nbsp; pDoc-&gt;m_tracker.Draw (pDC);<BR>&nbsp;&nbsp;&nbsp;&nbsp; //Select blue brush out of device context.<BR>&nbsp;&nbsp;&nbsp;&nbsp; pDC-&gt;SelectObject(pOldBrush);<BR>}<BR>&nbsp;&nbsp;&nbsp; 最后，视图类中处理WM_LBUTTONDOWN消息，并增加下述代码。该段代码根据鼠标击键情况可以拖放、移动或者重置椭圆的大小。</P>
<P>void CTestView::OnLButtonDown(UINT nFlags, CPoint point) <BR>{<BR>&nbsp;&nbsp; //Get pointer to document.<BR>&nbsp;&nbsp;&nbsp;&nbsp; CTestDoc* pDoc=GetDocument();<BR>&nbsp;&nbsp;&nbsp;&nbsp; ASSERT_VALID (pDoc);</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp; //If clicked on ellipse, drag or resize it. Otherwise create a<BR>&nbsp;&nbsp;&nbsp;&nbsp; //rubber-band rectangle nd create a new ellipse.<BR>&nbsp;&nbsp;&nbsp;&nbsp; BOOL bResult=pDoc-&gt;m_tracker.HitTest (point)!=<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CRectTracker::hitNothing;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp; //Tracker rectangle changed so update views.<BR>&nbsp;&nbsp;&nbsp;&nbsp; if (bResult)<BR>&nbsp;&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pDoc-&gt;m_tracker.Track (this,point,TRUE);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pDoc-&gt;SetModifiedFlag ();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pDoc-&gt;UpdateAllViews (NULL);<BR>&nbsp;&nbsp;&nbsp;&nbsp; }<BR>&nbsp;&nbsp;&nbsp;&nbsp; else<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pDoc-&gt;m_tracker.TrackRubberBand (this,point,TRUE);</P>
<P>&nbsp;CView::OnLButtonDown(nFlags, point);<BR>}</P>
<P><STRONG>十二、如何在临时目录创建一个临时文件</STRONG><BR>如果你要在临时目录下创建临时文件，下面的代码能帮到你的忙。<BR>bool GetuniqueTempName (CString&amp; strTempName)<BR>{<BR>&nbsp; strTempName="";<BR>&nbsp;&nbsp;&nbsp;&nbsp; //Get the temporary files directory.<BR>&nbsp;&nbsp;&nbsp;&nbsp; TCHAR szTempPath [MAX_PATH];<BR>&nbsp;&nbsp;&nbsp;&nbsp; DWORD dwResult=:: GetTempPath (MAX_PATH, szTempPath);<BR>&nbsp;&nbsp;&nbsp;&nbsp; if (dwResult==0) <BR>&nbsp;&nbsp; return false;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp; //Create a unique temporary file.<BR>&nbsp;&nbsp;&nbsp;&nbsp; TCHAR szTempFile[MAX_PATH];<BR>&nbsp;&nbsp;&nbsp;&nbsp; UINT nResult=GetTempFileName (szTempPath, _T ("~ex"),0,szTempFile);<BR>&nbsp;&nbsp;&nbsp;&nbsp; if (dwResult==0) <BR>&nbsp;&nbsp; return false;</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp; strTempName=szTempFile;<BR>&nbsp; return true;<BR>}</P>
<P><STRONG>十三、如何限制窗口的最小范围</STRONG><BR>要限制窗体的大小，下面的代码能帮到你的忙。<BR>在CMainFrame中增加WM_GETMAXMININFO消息的处理函数，然后在这个函数中写代码如下:<BR>//限制主窗体的最小高度和宽度<BR>void CMainFrame::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) <BR>{<BR>&nbsp;lpMMI-&gt;ptMinTrackSize.x=600;<BR>&nbsp;lpMMI-&gt;ptMinTrackSize.y=400;<BR>&nbsp;CNewFrameWnd::OnGetMinMaxInfo(lpMMI);<BR>}</P>
<P><STRONG>十四、怎样删除文件到回收站中</STRONG><BR>&nbsp;要删除文件到回收站，很简单。只要用SHFileOperation函数就行了，下面的代码我将为你演示了这一个函数的用法。当然你可以直接拷贝到你的项目中。<BR>//删除文件到回收站中<BR>//pszPath&nbsp; : 待删除的全路径文件名<BR>//bDelete&nbsp; : TRUE 删除，不移到回收站，FALSE:移到回收站<BR>一、&nbsp;//返回&nbsp;&nbsp;&nbsp; : TRUE 删除成功&nbsp;&nbsp;&nbsp;&nbsp; FALSE 删除失败<BR>BOOL CDelFileToRecycleDlg::Recycle(LPCTSTR pszPath, BOOL bDelete/*=FALSE*/)<BR>{<BR>&nbsp;SHFILEOPSTRUCT&nbsp; shDelFile;<BR>&nbsp;memset(&amp;shDelFile,0,sizeof(SHFILEOPSTRUCT));<BR>&nbsp;shDelFile.fFlags |= FOF_SILENT;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // don"t report progress<BR>&nbsp;shDelFile.fFlags |= FOF_NOERRORUI;&nbsp;&nbsp;&nbsp;&nbsp; // don"t report errors<BR>&nbsp;shDelFile.fFlags |= FOF_NOCONFIRMATION;&nbsp;&nbsp;&nbsp; // don"t confirm delete<BR>&nbsp;// Copy pathname to double-NULL-terminated string.<BR>&nbsp;//<BR>&nbsp;TCHAR buf[_MAX_PATH + 1]; // allow one more character<BR>&nbsp;_tcscpy(buf, pszPath);&nbsp;&nbsp; // copy caller"s pathname<BR>&nbsp;buf[_tcslen(buf)+1]=0;&nbsp;&nbsp; // need two NULLs at end</P>
<P>&nbsp;// Set SHFILEOPSTRUCT params for delete operation<BR>&nbsp;shDelFile.wFunc = FO_DELETE;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // REQUIRED: delete operation<BR>&nbsp;shDelFile.pFrom = buf;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // REQUIRED: which file(s)<BR>&nbsp;shDelFile.pTo = NULL;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // MUST be NULL<BR>&nbsp;if (bDelete)<BR>&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // if delete requested..<BR>&nbsp;&nbsp;shDelFile.fFlags &amp;= ~FOF_ALLOWUNDO;&nbsp;&nbsp;&nbsp; // ..don"t use Recycle Bin<BR>&nbsp;} <BR>&nbsp;else <BR>&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // otherwise..<BR>&nbsp;&nbsp;shDelFile.fFlags |= FOF_ALLOWUNDO;&nbsp;&nbsp;&nbsp; // ..send to Recycle Bin<BR>&nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp; return SHFileOperation(&amp;shDelFile);&nbsp;&nbsp;&nbsp; // do it!<BR>}</P>
<P><STRONG>十五、内存泄漏检查</STRONG><BR>&nbsp;&nbsp;&nbsp; 也许你已经知道，在C++和C语言中指针问题也就是内存申请与释放是一个令人头疼的事情，假如你申请了内存，但没有释放，并且你的程序需要长时间地运行，那么，系统的资源将逐渐减少，当系统的资源全部被用完时，系统将会崩溃。所以在开发程序的过程中一定要保证资源的完全释放。下面我们来介绍内存漏洞的检查。<BR>示例如下：<BR>// do your memory allocations and deallocations...<BR>&nbsp;CString s = "This is a frame variable";<BR>#ifdef _DEBUG<BR>&nbsp;CMemoryState oldMemState, newMemState, diffMemState;<BR>&nbsp;oldMemState.Checkpoint();<BR>#endif<BR>&nbsp;// the next object is a heap object<BR>&nbsp;CString* p = new CString( "Smith&nbsp; Alan&nbsp; 581_0215" );<BR>&nbsp;delete p;<BR>&nbsp;p=NULL;<BR>#ifdef _DEBUG<BR>&nbsp;newMemState.Checkpoint();<BR>&nbsp;BOOL b=diffMemState.Difference(oldMemState, newMemState);<BR>&nbsp;if (b)<BR>&nbsp;{<BR>&nbsp;&nbsp;AfxMessageBox( "Memory leaked! " );<BR>&nbsp;}<BR>#endif<BR>&nbsp;&nbsp;&nbsp; 根据试验，由于我们无法释放掉象int CString char 申请的变量。只能释放指针型的变量。而检测内存时，照样会出现内存泄漏现象。所以，这种内存检测方式局限性还是很大。因为我们无法释放非指针型变量。</P><img src ="http://www.cppblog.com/apollo/aggbug/3461.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/apollo/" target="_blank">笑笑生</a> 2006-02-24 13:26 <a href="http://www.cppblog.com/apollo/articles/3461.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>