﻿<?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>BlogJava-The NoteBook of EricKong-随笔分类-C 编程</title><link>http://www.blogjava.net/jjshcc/category/54125.html</link><description>桑巴葡语翻译工作室是广州市最专业葡萄牙语翻译机构-www.puyufanyi.com-欢迎寻找Java,As400,Mainframe的猎头eric_cc#qq.ccom(把#换成@)</description><language>zh-cn</language><lastBuildDate>Thu, 16 Jan 2014 08:49:56 GMT</lastBuildDate><pubDate>Thu, 16 Jan 2014 08:49:56 GMT</pubDate><ttl>60</ttl><item><title>Gdb 调试器</title><link>http://www.blogjava.net/jjshcc/archive/2014/01/15/408982.html</link><dc:creator>Eric_jiang</dc:creator><author>Eric_jiang</author><pubDate>Wed, 15 Jan 2014 07:59:00 GMT</pubDate><guid>http://www.blogjava.net/jjshcc/archive/2014/01/15/408982.html</guid><wfw:comment>http://www.blogjava.net/jjshcc/comments/408982.html</wfw:comment><comments>http://www.blogjava.net/jjshcc/archive/2014/01/15/408982.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/jjshcc/comments/commentRss/408982.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jjshcc/services/trackbacks/408982.html</trackback:ping><description><![CDATA[<div><div>Gdb 调试器</div><div>调试是所有程序员都会面临的问题。如何提高程序员的调试效率，更好更快地定位程序中的问题从而加快程序开发的进度，是大家共同面对的。就如读者熟知的Windows下的一些调试工具，如VC自带的如设置断点、单步跟踪等，都受到了广大用户的赞赏。那么，在Linux下有什么很好的调试工具呢？</div><div>本文所介绍的Gdb 调试器是一款GNU 开发组织并发布的UNIX/Linux 下的程序调试工具。虽然，它没有图形化的友好界面，但是它强大的功能也足以与微软的VC 工具等媲美。</div><div>下面就请跟随笔者一步步学习Gdb调试器。</div><div>3.5.1 Gdb使用流程</div><div>这里给出了一个短小的程序，由此带领读者熟悉一下Gdb 的使用流程。建议读者能够实</div><div>际动手操作。</div><div>首先，打开Linux 下的编辑器Vi或者Emacs，编辑如下代码（由于为了更好地熟悉Gdb的操作，笔者在此使用Vi 编辑，希望读者能够参见3.3 节中对Vi 的介绍，并熟练使用Vi）。</div><div>/*test.c*/</div><div>#include &lt;stdio.h&gt;</div><div>int sum(int m);</div><div>int main()</div><div>{</div><div>int i,n=0;</div><div>sum(50);</div><div>for(i=1; i&lt;=50; i++)</div><div>{</div><div>n += i;</div><div>}</div><div>printf("The sum of 1-50 is %d \n", n );</div><div>}</div><div>int sum(int m)</div><div>{</div><div>int i,n=0;</div><div>for(i=1; i&lt;=m;i++)</div><div>n += i;</div><div>printf("The sum of 1-m is %d\n", n);</div><div>}</div><div>在保存退出后首先使用Gcc对test.c进行编译，注意一定要加上选项&#8220;-g&#8221;，这样编译出的可执行代码中才包含调试信息，否则之后Gdb 无法载入该可执行文件。</div><div>[root@localhost Gdb]# gcc -g test.c -o test</div><div>虽然这段程序没有错误，但调试完全正确的程序可以更加了解Gdb 的使用流程。接下来就启动Gdb 进行调试。注意，Gdb 进行调试的是可执行文件，而不是如&#8220;.c&#8221;的源代码，因此，需要先通过Gcc编译生成可执行文件才能用Gdb进行调试。</div><div>[root@localhost Gdb]# gdb test</div><div>GNU Gdb Red Hat Linux (6.3.0.0-1.21rh)</div><div>Copyright 2004 Free Software Foundation, Inc.</div><div>GDB is free software, covered by the GNU General Public License, and you are</div><div>welcome to change it and/or distribute copies of it under certain conditions.</div><div>Type "show copying" to see the conditions.</div><div>There is absolutely no warranty for GDB. Type "show warranty" for details.</div><div>This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db</div><div>library "/lib/libthread_db.so.1".</div><div>(gdb)</div><div>可以看出，在Gdb 的启动画面中指出了Gdb 的版本号、使用的库文件等信息，接下来就进入了由&#8220;（gdb）&#8221;开头的命令行界面了。</div><div>（1）查看文件</div><div>在 Gdb 中键入&#8220;l&#8221;（list）就可以查看所载入的文件，如下所示：</div><div>注意</div><div>在Gdb的命令中都可使用缩略形式的命令，如&#8220;l&#8221;代便&#8220;list&#8221;，&#8220;b&#8221;代表&#8220;breakpoint&#8221;，&#8220;p&#8221;代表&#8220;print&#8221;等，读者也可使用&#8220;help&#8221;命令查看帮助信息。</div><div>(Gdb) l</div><div>1 #include &lt;stdio.h&gt;</div><div>2 int sum(int m);</div><div>3 int main()</div><div>4 {</div><div>5 int i,n=0;</div><div>6 sum(50);</div><div>7 for(i=1; i&lt;=50; i++)</div><div>8 {</div><div>9 n += i;</div><div>10 }</div><div>(Gdb) l</div><div>11 printf("The sum of 1～50 is %d \n", n );</div><div>12</div><div>13 }</div><div>14 int sum(int m)</div><div>15 {</div><div>16 int i,n=0;</div><div>17 for(i=1; i&lt;=m;i++)</div><div>18 n += i;</div><div>19 printf("The sum of 1～m is = %d\n", n);</div><div>20 }</div><div>可以看出，Gdb 列出的源代码中明确地给出了对应的行号，这样就可以大大地方便代码的定位。</div><div>（2）设置断点</div><div>设置断点是调试程序中是一个非常重要的手段，它可以使程序到一定位置暂停它的运行。因此，程序员在该位置处可以方便地查看变量的值、堆栈情况等，从而找出代码的症结所在。</div><div>在 Gdb 中设置断点非常简单，只需在&#8220;b&#8221;后加入对应的行号即可（这是最常用的方式，另外还有其他方式设置断点）。如下所示：</div><div>(Gdb) b 6</div><div>Breakpoint 1 at 0x804846d: file test.c, line 6.</div><div>要注意的是，在Gdb 中利用行号设置断点是指代码运行到对应行之前将其停止，如上例中，代码运行到第5行之前暂停（并没有运行第5行）。</div><div>（3）查看断点情况</div><div>在设置完断点之后，用户可以键入&#8220;info b&#8221;来查看设置断点情况，在Gdb 中可以设置</div><div>多个断点。</div><div>(Gdb) info b</div><div>Num Type Disp Enb Address What</div><div>1 breakpoint keep y 0x0804846d in main at test.c:6</div><div>（4）运行代码</div><div>接下来就可运行代码了，Gdb默认从首行开始运行代码，可键入&#8220;r&#8221;（run）即可（若想从程序中指定行开始运行，可在r 后面加上行号）。</div><div>(Gdb) r</div><div>Starting program: /root/workplace/Gdb/test</div><div>Reading symbols from shared object read from target memory...done.</div><div>Loaded system supplied DSO at 0x5fb000</div><div>Breakpoint 1, main () at test.c:6</div><div>6 sum(50);</div><div>可以看到，程序运行到断点处就停止了。</div><div>（5）查看变量值</div><div>在程序停止运行之后，程序员所要做的工作是查看断点处的相关变量值。在Gdb 中只需</div><div>键入&#8220;p&#8221;＋变量值即可，如下所示：</div><div>(Gdb) p n</div><div>$1 = 0</div><div>(Gdb) p i</div><div>$2 = 134518440</div><div>在此处，为什么变量&#8220;i&#8221;的值为如此奇怪的一个数字呢？原因就在于程序是在断点设置的对应行之前停止的，那么在此时，并没有把&#8220;i&#8221;的数值赋为零，而只是一个随机的数字。但变量&#8220;n&#8221;是在第四行赋值的，故在此时已经为零。</div><div>小技巧</div><div>Gdb 在显示变量值时都会在对应值之前加上&#8220;$N&#8221;标记，它是当前变量值的引用标记，所以以后若想再次引用此变量就可以直接写作&#8220;$N&#8221;，而无需写冗长的变量名。</div><div>（6）单步运行</div><div>单步运行可以使用命令&#8220;n&#8221;（next）或&#8220;s&#8221;（step），它们之间的区别在于：若有函数调用的时候，&#8220;s&#8221;会进入该函数而&#8220;n&#8221;不会进入该函数。因此，&#8220;s&#8221;就类似于VC等工具中的</div><div>&#8220;step in&#8221;，&#8220;n&#8221;类似与VC等工具中的&#8220;step over&#8221;。它们的使用如下所示：</div><div>(Gdb) n</div><div>The sum of 1-m is 1275</div><div>7 for(i=1; i&lt;=50; i++)</div><div>(Gdb) s</div><div>sum (m=50) at test.c:16</div><div>16 int i,n=0;</div><div>可见，使用&#8220;n&#8221;后，程序显示函数sum的运行结果并向下执行，而使用&#8220;s&#8221;后则进入到sum函数之中单步运行。</div><div>（7）恢复程序运行</div><div>在查看完所需变量及堆栈情况后，就可以使用命令&#8220;c&#8221;（continue）恢复程序的正常运行了。这时，它会把剩余还未执行的程序执行完，并显示剩余程序中的执行结果。以下是之前使用&#8220;n&#8221;命令恢复后的执行结果：</div><div>(Gdb) c</div><div>Continuing.</div><div>The sum of 1-50 is :1275</div><div>Program exited with code 031.</div><div>可以看出，程序在运行完后退出，之后程序处于&#8220;停止状态&#8221;。</div><div>小知识</div><div>在Gdb中，程序的运行状态有&#8220;运行&#8221;、&#8220;暂停&#8221;和&#8220;停止&#8221;3种，其中&#8220;暂停&#8221;状态为程序遇到了断点或观察点之类的，程序暂时停止运行，而此时函数的地址、函数参数、函数内的局部变量都会被压入&#8220;栈&#8221;（Stack）中。故在这种状态下可以查看函数的变量值等各种属性。但在函数处于&#8220;停止&#8221;状态之后，&#8220;栈&#8221;就会自动撤销，它也就无法查看各种信息了。</div><div>3.5.2 Gdb基本命令</div><div>Gdb 的命令可以通过查看help 进行查找，由于Gdb 的命令很多，因此Gdb 的help 将其分成了很多种类（class），用户可以通过进一步查看相关class找到相应命令。如下所示：</div><div>(gdb) help</div><div>List of classes of commands:</div><div>aliases -- Aliases of other commands</div><div>breakpoints -- Making program stop at certain points</div><div>data -- Examining data</div><div>files -- Specifying and examining files</div><div>internals -- Maintenance commands</div><div>&#8230;</div><div>Type "help" followed by a class name for a list of commands in that class.</div><div>Type "help" followed by command name for full documentation.</div><div>Command name abbreViations are allowed if unambiguous.</div><div>上述列出了Gdb 各个分类的命令，注意底部的加粗部分说明其为分类命令。接下来可以</div><div>具体查找各分类种的命令。如下所示：</div><div>(gdb) help data</div><div>Examining data.</div><div>List of commands:</div><div>call -- Call a function in the program</div><div>delete display -- Cancel some expressions to be displayed when program stops</div><div>delete mem -- Delete memory region</div><div>disable display -- Disable some expressions to be displayed when program stops</div><div>&#8230;</div><div>Type "help" followed by command name for full documentation.</div><div>Command name abbreViations are allowed if unambiguous.</div><div>至此，若用户想要查找call命令，就可键入&#8220;help call&#8221;。</div><div>(gdb) help call</div><div>Call a function in the program.</div><div>The argument is the function name and arguments, in the notation of the</div><div>current working language. The result is printed and saved in the value</div><div>history, if it is not void.</div><div>当然，若用户已知命令名，直接键入&#8220;help [command]&#8221;也是可以的。</div><div>Gdb 中的命令主要分为以下几类：工作环境相关命令、设置断点与恢复命令、源代码查看命令、查看运行数据相关命令及修改运行参数命令。以下就分别对这几类的命令进行讲解。</div><div>1．工作环境相关命令</div><div>Gdb中不仅可以调试所运行的程序，而且还可以对程序相关的工作环境进行相应的设定，甚至还可以使用shell 中的命令进行相关的操作，其功能极其强大。表3.10 所示为Gdb 常见工作环境相关命令。</div><div>表3.10 Gdb 工作环境相关命令</div><div>命令格式 <span style="white-space:pre">	</span>含义</div><div>set args<span style="white-space:pre">	</span>运行时的参数指定运行时参数，如set args 2</div><div>show args <span style="white-space:pre">	</span>查看设置好的运行参数</div><div>path dir <span style="white-space:pre">	</span>设定程序的运行路径</div><div>show paths <span style="white-space:pre">	</span>查看程序的运行路径</div><div>set enVironment var [=value] 设置环境变量</div><div>show enVironment [var] <span style="white-space:pre">		</span> 查看环境变量</div><div>cd dir 进入到dir目录，相当于shell中的cd命令</div><div>pwd 显示当前工作目录</div><div>shell command 运行shell的command命令</div><div>2．设置断点与恢复命令</div><div>Gdb 中设置断点与恢复的常见命令如表3.11 所示。</div><div>表 3.11 Gdb 设置断点与恢复相关命令</div><div>命令格式 &nbsp;含义</div><div>bnfo b <span style="white-space:pre">	</span>查看所设断点</div><div>break <span style="white-space:pre">	</span>行号或函数名 &lt;条件表达式&gt; 设置断点</div><div>tbreak <span style="white-space:pre">	</span>行号或函数名 &lt;条件表达式&gt; 设置临时断点，到达后被自动删除</div><div>delete <span style="white-space:pre">	</span>[断点号] 删除指定断点，其断点号为&#8220;info b&#8221;中的第一栏。若缺省断点号则删除所有断点</div><div>disable [断点号]] 停止指定断点，使用&#8220;info b&#8221;仍能查看此断点。同delete一样，省断点号则停止所有断点</div><div>enable &nbsp;[断点号] 激活指定断点，即激活被disable停止的断点</div><div>condition [断点号] &lt;条件表达式&gt; 修改对应断点的条件</div><div>ignore <span style="white-space:pre">		</span>[断点号]&lt;num&gt; 在程序执行中，忽略对应断点num次</div><div>step <span style="white-space:pre">	</span>单步恢复程序运行，且进入函数调用</div><div>next<span style="white-space:pre">	</span> 单步恢复程序运行，但不进入函数调用</div><div>finish <span style="white-space:pre">	</span>运行程序，直到当前函数完成返回</div><div>c 继续执行函数，直到函数结束或遇到新的断点</div><div>由于设置断点在Gdb 的调试中非常重要，所以在此再着重讲解一下Gdb中设置断点的方法。Gdb 中设置断点有多种方式：其一是按行设置断点，设置方法在3.5.1节已经指出，在此就不重复了。另外还可以设置函数断点和条件断点，在此结合上一小节的代码，具体介绍后两种设置断点的方法。</div><div>&#9312; 函数断点</div><div>Gdb 中按函数设置断点只需把函数名列在命令&#8220;b&#8221;之后，如下所示：</div><div>(gdb) b sum</div><div>Breakpoint 1 at 0x80484ba: file test.c, line 16.</div><div>(gdb) info b</div><div>Num Type Disp Enb Address What</div><div>1 breakpoint keep y 0x080484ba in sum at test.c:16</div><div>要注意的是，此时的断点实际是在函数的定义处，也就是在16 行处（注意第16 行还未</div><div>执行）。</div><div>&#9313; 条件断点</div><div>Gdb 中设置条件断点的格式为：b 行数或函数名if 表达式。具体实例如下所示：</div><div>(gdb) b 8 if i==10</div><div>Breakpoint 1 at 0x804848c: file test.c, line 8.</div><div>(gdb) info b</div><div>Num Type Disp Enb Address What</div><div>1 breakpoint keep y 0x0804848c in main at test.c:8</div><div>stop only if i == 10</div><div>(gdb) r</div><div>Starting program: /home/yul/test</div><div>The sum of 1-m is 1275</div><div>Breakpoint 1, main () at test.c:9</div><div>9 n += i;</div><div>(gdb) p i</div><div>$1 = 10</div><div>可以看到，该例中在第8 行（也就是运行完第7 行的for 循环）设置了一个&#8220;i==0&#8221;的条件断点，在程序运行之后可以看出，程序确实在i为10 时暂停运行。</div><div>3．Gdb 中源码查看相关命令</div><div>在 Gdb 中可以查看源码以方便其他操作，它的常见相关命令如表3.12 所示。</div><div>表3.12 Gdb 源码查看相关相关命令</div><div>命令格式 <span style="white-space:pre">		</span>含义</div><div>list <span style="white-space:pre">			</span>&lt;行号&gt;|&lt;函数名&gt; 查看指定位置代码</div><div>file <span style="white-space:pre">			</span>[文件名] 加载指定文件</div><div>forward-search &nbsp;正则表达式源代码前向搜索</div><div>reverse-search &nbsp;正则表达式源代码后向搜索</div><div>dir dir <span style="white-space:pre">		</span>停止路径名</div><div>show directories 显示定义了的源文件搜索路径</div><div>info line<span style="white-space:pre">		</span> 显示加载到Gdb内存中的代码</div><div>4．Gdb 中查看运行数据相关命令</div><div>Gdb 中查看运行数据是指当程序处于&#8220;运行&#8221;或&#8220;暂停&#8221;状态时，可以查看的变量及表</div><div>达式的信息，其常见命令如表3.13 所示：</div><div>表3.13 Gdb 查看运行数据相关命令</div><div>命令格式 <span style="white-space:pre">	</span>含义</div><div>print <span style="white-space:pre">		</span>表达式|变量查看程序运行时对应表达式和变量的值</div><div>x &lt;n/f/u&gt; &nbsp; 查看内存变量内容。其中n为整数表示显示内存的长度，f表示显示的格式，u表示从当前地址往后请求显示的字节数</div><div>display <span style="white-space:pre">	</span>表达式设定在单步运行或其他情况中，自动显示的对应表达式的内容</div><div>5．Gdb 中修改运行参数相关命令</div><div>Gdb 还可以修改运行时的参数，并使该变量按照用户当前输入的值继续运行。它的设置方法为：在单步执行的过程中，键入命令&#8220;set 变量＝设定值&#8221;。这样，在此之后，程序就会按照该设定的值运行了。下面，笔者结合上一节的代码将n的初始值设为4，其代码如</div><div>下所示：</div><div>(Gdb) b 7</div><div>Breakpoint 5 at 0x804847a: file test.c, line 7.</div><div>(Gdb) r</div><div>Starting program: /home/yul/test</div><div>The sum of 1-m is 1275</div><div>Breakpoint 5, main () at test.c:7</div><div>7 for(i=1; i&lt;=50; i++)</div><div>(Gdb) set n=4</div><div>(Gdb) c</div><div>Continuing.</div><div>The sum of 1-50 is 1279</div><div>Program exited with code 031.</div><div>可以看到，最后的运行结果确实比之前的值大了4。</div><div>Gdb的使用切记点：</div><div>&#183; 在Gcc编译选项中一定要加入&#8220;-g&#8221;。</div><div>&#183; 只有在代码处于&#8220;运行&#8221;或&#8220;暂停&#8221;状态时才能查看变量值。</div><div>&#183; 设置断点后程序在指定行之前停</div></div><img src ="http://www.blogjava.net/jjshcc/aggbug/408982.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jjshcc/" target="_blank">Eric_jiang</a> 2014-01-15 15:59 <a href="http://www.blogjava.net/jjshcc/archive/2014/01/15/408982.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Gcc 编译器</title><link>http://www.blogjava.net/jjshcc/archive/2014/01/15/408978.html</link><dc:creator>Eric_jiang</dc:creator><author>Eric_jiang</author><pubDate>Wed, 15 Jan 2014 07:21:00 GMT</pubDate><guid>http://www.blogjava.net/jjshcc/archive/2014/01/15/408978.html</guid><wfw:comment>http://www.blogjava.net/jjshcc/comments/408978.html</wfw:comment><comments>http://www.blogjava.net/jjshcc/archive/2014/01/15/408978.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jjshcc/comments/commentRss/408978.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jjshcc/services/trackbacks/408978.html</trackback:ping><description><![CDATA[<div><div></div><div>3.4 Gcc 编译器</div><div>GNU CC（简称为Gcc）是GNU项目中符合ANSI C 标准的编译系统，能够编译用C、C++和Object C等语言编写的程序。Gcc不仅功能强大，而且可以编译如C、C++、Object C、Java、Fortran、Pascal、Modula-3 和Ada 等多种语言，而且Gcc 又是一个交叉平台编译器，它能够在当前CPU平台上为多种不同体系结构的硬件平台开发软件，因此尤其适合在嵌入式领域的开发编译。本章中的示例，除非特别注明，否则均采用Gcc版本为4.0.0。</div><div>下表3.6 是Gcc支持编译源文件的后缀及其解释。</div><div>表3.6 Gcc所支持后缀名解释</div><div>后缀名 <span style="white-space:pre">		</span>所对应的语言 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;后缀名 所对应的语言</div><div>.c <span style="white-space:pre">			</span>C原始程序 &nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .s/.S &nbsp;汇编语言原始程序</div><div>.C/.cc/.cxx &nbsp; &nbsp; &nbsp; &nbsp;C++原始程序&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .h &nbsp; &nbsp; &nbsp; 预处理文件（头文件）</div><div>.m &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Objective-C原始程序 &nbsp;.o &nbsp; &nbsp; &nbsp; 目标文件</div><div>.i 已经过预处理的C原始程序 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;.a/.so &nbsp;&nbsp;&nbsp;&nbsp;编译后的库文件</div><div>.ii 已经过预处理的C++原始程序</div><div>3.4.1 Gcc编译流程解析</div><div>如本章开头提到的，<strong style="color: red; ">Gcc的编译流程分为了4个步骤</strong>，分别为：</div><div><span style="color: red; "><strong>&#183; 预处理（Pre-Processing）；</strong></span></div><div><span style="color: red; "><strong>&#183; 编译（Compiling）；</strong></span></div><div><span style="color: red; "><strong>&#183; 汇编（Assembling）；</strong></span></div><div><span style="color: red; "><strong>&#183; 链接（Linking）。</strong></span></div><div>下面就具体来查看一下Gcc是如何完成4 个步骤的。</div><div>首先，有以下hello.c源代码：</div><div>#include&lt;stdio.h&gt;</div><div>int main()</div><div>{</div><div>printf("Hello! This is our embedded world!\n");</div><div>return 0;</div><div>}</div><div><span style="color: red; "><strong>（1）预处理阶段</strong></span></div><div>在该阶段，编译器将上述代码中的stdio.h编译进来，并且用户可以使用Gcc的选项&#8220;-E&#8221;进行查看，该选项的作用是让Gcc在预处理结束后停止编译过程。</div><div>注意</div><div>Gcc指令的一般格式为：Gcc [选项] 要编译的文件 [选项] [目标文件]</div><div>其中，目标文件可缺省，Gcc默认生成可执行的文件，命为：编译文件.out</div><div>[root@localhost Gcc]# <span style="color: red; "><strong>Gcc &#8211;E hello.c &#8211;o hello.i</strong></span></div><div>在此处，选项&#8220;-o&#8221;是指目标文件，由表3.6 可知，&#8220;.i&#8221;文件为已经过预处理的C 原始程序。以下列出了hello.i文件的部分内容：</div><div>typedef int (*__gconv_trans_fct) (struct __gconv_step *,</div><div>struct __gconv_step_data *, void *,</div><div>__const unsigned char *,</div><div>__const unsigned char **,</div><div>__const unsigned char *, unsigned char **,</div><div>size_t *);</div><div>&#8230;</div><div># 2 "hello.c" 2</div><div>int main()</div><div>{</div><div>printf("Hello! This is our embedded world!\n");</div><div>return 0;</div><div>}</div><div>由此可见，Gcc确实进行了预处理，它把&#8220;stdio.h&#8221;的内容插入到hello.i文件中。</div><div><span style="color: red; "><strong>（2）编译阶段</strong></span></div><div>接下来进行的是编译阶段，在这个阶段中，Gcc 首先要检查代码的规范性、是否有语法错误等，以确定代码的实际要做的工作，在检查无误后，Gcc把代码翻译成汇编语言。用户可以使用&#8220;-S&#8221;选项来进行查看，该选项只进行编译而不进行汇编，生成汇编代码。</div><div>[root@localhost Gcc]# <span style="color: red; "><strong>Gcc &#8211;S hello.i &#8211;o hello.s</strong></span></div><div>以下列出了hello.s的内容，可见Gcc已经将其转化为汇编了，感兴趣的读者可以分析一下这一行简单的C语言小程序是如何用汇编代码实现的。</div><div>.file "hello.c"</div><div>.section .rodata</div><div>.align 4</div><div>.LC0:</div><div>.string "Hello! This is our embedded world!"</div><div>.text</div><div>.globl main</div><div>.type main, @function</div><div>main:</div><div>pushl %ebp</div><div>movl %esp, %ebp</div><div>subl $8, %esp</div><div>andl $-16, %esp</div><div>movl $0, %eax</div><div>addl $15, %eax</div><div>addl $15, %eax</div><div>shrl $4, %eax</div><div>sall $4, %eax</div><div>subl %eax, %esp</div><div>subl $12, %esp</div><div>pushl $.LC0</div><div>call puts</div><div>addl $16, %esp</div><div>movl $0, %eax</div><div>leave</div><div>ret</div><div>.size main, .-main</div><div>.ident "GCC: (GNU) 4.0.0 20050519 (Red Hat 4.0.0-8)"</div><div>.section .note.GNU-stack,"",@progbits</div><div><span style="color: red; "><strong>（3）汇编阶段</strong></span></div><div>汇编阶段是把编译阶段生成的&#8220;.s&#8221;文件转成目标文件，读者在此可使用选项&#8220;-c&#8221;就可看到汇编代码已转化为&#8220;.o&#8221;的二进制目标代码了。如下所示：</div><div>[root@localhost Gcc]# <span style="color: red; "><strong>Gcc &#8211;c hello.s &#8211;o hello.o</strong></span></div><div><span style="color: red; "><strong>（4）链接阶段</strong></span></div><div>在成功编译之后，就进入了链接阶段。在这里涉及到一个重要的概念：函数库。读者可以重新查看这个小程序，在这个程序中并没有定义&#8220;printf&#8221;的函数实现，且在预编译中包含进的&#8220;stdio.h&#8221;中也只有该函数的声明，而没有定义函数的实现，那么，是在哪里实现&#8220;printf&#8221;函数的呢？最后的答案是：系统把这些函数实现都被做到名为libc.so.6的库文件中去了，在没有特别指定时，Gcc会到系统默认的搜索路径&#8220;/usr/lib&#8221;下进行查找，也就是链接到libc.so.6库函数中去，这样就能实现函数&#8220;printf&#8221;了，而这也就是链接的作用。函数库一般分为静态库和动态库两种。静态库是指编译链接时，把库文件的代码全部加入到可执行文件中，因此生成的文件比较大，但在运行时也就不再需要库文件了。其后缀名一般为&#8220;.a&#8221;。动态库与之相反，在编译链接时并没有把库文件的代码加入到可执行文件中，而是在程序执行时由运行时链接文件加载库，这样可以节省系统的开销。动态库一般后缀名为&#8220;.so&#8221;，如前面所述的libc.so.6就是动态库。Gcc在编译时默认使用动态库。完成了链接之后，Gcc就可以生成可执行文件，如下所示。</div><div>[root@localhost Gcc]# <span style="color: red; "><strong>Gcc hello.o &#8211;o hello</strong></span></div><div>运行该可执行文件，出现正确的结果如下。</div><div>[root@localhost Gcc]# ./hello</div><div>Hello! This is our embedded world!</div><div>3.4.2 Gcc编译选项分析</div><div>Gcc 有超过100 个的可用选项，主要包括总体选项、告警和出错选项、优化选项和体系</div><div>结构相关选项。以下对每一类中最常用的选项进行讲解。</div><div>（1）总体选项</div><div>Gcc的总结选项如表3.7 所示，很多在前面的示例中已经有所涉及。</div><div><span style="color: red; "><strong>表3.7 Gcc总体选项列表</strong></span></div><div><span style="color: red; "><strong>-c 只是编译不链接，生成目标文件&#8220;.o&#8221;</strong></span></div><div><span style="color: red; "><strong>-S 只是编译不汇编，生成汇编代码</strong></span></div><div><span style="color: red; "><strong>-E 只进行预编译，不做其他处理</strong></span></div><div><span style="color: red; "><strong>-g 在可执行程序中包含标准调试信息</strong></span></div><div><span style="color: red; "><strong>-o file 把输出文件输出到file里</strong></span></div><div><span style="color: red; "><strong>-v 打印出编译器内部编译各过程的命令行信息和编译器的版本</strong></span></div><div><span style="color: red; "><strong>-I dir 在头文件的搜索路径列表中添加dir目录</strong></span></div><div><span style="color: red; "><strong>-L dir 在库文件的搜索路径列表中添加dir目录</strong></span></div><div><span style="color: red; "><strong>-static 链接静态库</strong></span></div><div><span style="color: red; "><strong>-llibrary 连接名为library的库文件</strong></span></div><div>对于&#8220;-c&#8221;、&#8220;-E&#8221;、&#8220;-o&#8221;、&#8220;-S&#8221;选项在前一小节中已经讲解了其使用方法，在此主要讲解另外两个非常常用的库依赖选项&#8220;-I dir&#8221;和&#8220;-L dir&#8221;。</div><div>&#183; &#8220;-I dir&#8221;</div><div>正如上表中所述，&#8220;-I dir&#8221;选项可以在头文件的搜索路径列表中添加dir 目录。由于Linux中头文件都默认放到了&#8220;/usr/include/&#8221;目录下，因此，当用户希望添加放置在其他位置的头文件时，就可以通过&#8220;-I dir&#8221;选项来指定，这样，Gcc就会到相应的位置查找对应的目录。比如在&#8220;/root/workplace/Gcc&#8221;下有两个文件：</div><div>/*hello1.c*/</div><div>#include&lt;my.h&gt;</div><div>int main()</div><div>{</div><div>printf("Hello!!\n");</div><div>return 0;</div><div>}</div><div>/*my.h*/</div><div>#include&lt;stdio.h&gt;</div><div>这样，就可在Gcc命令行中加入&#8220;-I&#8221;选项：</div><div>[root@localhost Gcc] Gcc hello1.c &#8211;I /root/workplace/Gcc/ -o hello1</div><div>这样，Gcc就能够执行出正确结果。</div><div>小知识</div><div>在include语句中，&#8220;&lt;&gt;&#8221;表示在标准路径中搜索头文件，&#8220;&#8220;&#8221;&#8221;表示在本目录中搜索。故在上例中，可把hello1.c的&#8220;#include&lt;my.h&gt;&#8221;改为&#8220;#include &#8220;my.h&#8221;&#8221;，就不需要加上&#8220;-I&#8221;选项了。</div><div>&#183;<span style="color: red; "><strong> &#8220;-L dir&#8221;选项&#8220;-L dir&#8221;的功能与&#8220;-I dir&#8221;类似，能够在库文件的搜索路径列表中添加dir 目录。</strong></span></div><div><span style="color: red; "><strong>例如有程序hello_sq.c需要用到目录&#8220;/root/workplace/Gcc/lib&#8221;下的一个动态库libsunq.so，则</strong></span></div><div>只需键入如下命令即可：</div><div>[root@localhost Gcc] Gcc hello_sq.c &#8211;L /root/workplace/Gcc/lib &#8211;lsunq &#8211;o hello_sq</div><div>需要注意的是，&#8220;-I dir&#8221;和&#8220;-L dir&#8221;都只是指定了路径，而没有指定文件，因此不能在路径中包含文件名。</div><div>另外值得详细解释一下的是&#8220;-l&#8221;选项，它指示Gcc去连接库文件libsunq.so。由于在Linux下的库文件命名时有一个规定：必须以l、i、b 3 个字母开头。因此在用-l选项指定链接的库文件名时可以省去l、i、b 3个字母。也就是说Gcc在对&#8220;-lsunq&#8221;进行处理时，会自动去链接名为libsunq.so的文件。</div><div>（2）告警和出错选项</div><div>Gcc的告警和出错选项如表3.8 所示。</div><div>表3.8 Gcc总体选项列表</div><div>选 项 含 义</div><div>-ansi 支持符合ANSI标准的C程序</div><div>-pedantic 允许发出ANSI C标准所列的全部警告信息</div><div>续表</div><div>选 项 含 义</div><div>-pedantic-error 允许发出ANSI C标准所列的全部错误信息</div><div>-w 关闭所有告警</div><div>-Wall 允许发出Gcc提供的所有有用的报警信息</div><div>-werror 把所有的告警信息转化为错误信息，并在告警发生时终止编译过程</div><div>下面结合实例对这几个告警和出错选项进行简单的讲解。</div><div>如有以下程序段：</div><div>#include&lt;stdio.h&gt;</div><div>void main()</div><div>{</div><div>long long tmp = 1;</div><div>printf("This is a bad code!\n");</div><div>return 0;</div><div>}</div><div>这是一个很糟糕的程序，读者可以考虑一下有哪些问题？</div><div>&#183; &#8220;-ansi&#8221;</div><div>该选项强制Gcc生成标准语法所要求的告警信息，尽管这还并不能保证所有没有警告的程序都是符合ANSI C标准的。运行结果如下所示：</div><div>[root@localhost Gcc]# Gcc &#8211;ansi warning.c &#8211;o warning</div><div>warning.c: 在函数&#8220;main&#8221;中：</div><div>warning.c:7 警告：在无返回值的函数中，&#8220;return&#8221;带返回值</div><div>warning.c:4 警告：&#8220;main&#8221;的返回类型不是&#8220;int&#8221;</div><div>可以看出，该选项并没有发现&#8220;long long&#8221;这个无效数据类型的错误。</div><div>&#183; &#8220;-pedantic&#8221;</div><div>允许发出ANSI C标准所列的全部警告信息，同样也保证所有没有警告的程序都是符合</div><div>ANSI C标准的。其运行结果如下所示：</div><div>[root@localhost Gcc]# Gcc &#8211;pedantic warning.c &#8211;o warning</div><div>warning.c: 在函数&#8220;main&#8221;中：</div><div>warning.c:5 警告：ISO C90不支持&#8220;long long&#8221;</div><div>warning.c:7 警告：在无返回值的函数中，&#8220;return&#8221;带返回值</div><div>warning.c:4 警告：&#8220;main&#8221;的返回类型不是&#8220;int&#8221;</div><div>可以看出，使用该选项查看出了&#8220;long long&#8221;这个无效数据类型的错误。</div><div>&#183; &#8220;-Wall&#8221;</div><div>允许发出Gcc能够提供的所有有用的报警信息。该选项的运行结果如下所示：</div><div>[root@localhost Gcc]# Gcc &#8211;Wall warning.c &#8211;o warning</div><div>warning.c:4 警告：&#8220;main&#8221;的返回类型不是&#8220;int&#8221;</div><div>warning.c: 在函数&#8220;main&#8221;中：</div><div>warning.c:7 警告：在无返回值的函数中，&#8220;return&#8221;带返回值</div><div>warning.c:5 警告：未使用的变量&#8220;tmp&#8221;</div><div>使用&#8220;-Wall&#8221;选项找出了未使用的变量tmp，但它并没有找出无效数据类型的错误。</div><div>另外，Gcc 还可以利用选项对单独的常见错误分别指定警告，有关具体选项的含义感兴</div><div>趣的读者可以查看Gcc手册进行学习。</div><div>（3）优化选项</div><div>Gcc可以对代码进行优化，它通过编译选项&#8220;-On&#8221;来控制优化代码的生成，其中n是一个代表优化级别的整数。对于不同版本的Gcc 来讲，n 的取值范围及其对应的优化效果可能并不完全相同，比较典型的范围是从0变化到2或3。</div><div>不同的优化级别对应不同的优化处理工作。如使用优化选项&#8220;-O&#8221;主要进行线程跳转（Thread Jump）和延迟退栈（Deferred Stack Pops）两种优化。使用优化选项&#8220;-O2&#8221;除了完成所有&#8220;-O1&#8221;级别的优化之外，同时还要进行一些额外的调整工作，如处理器指令调度等。选项&#8220;-O3&#8221;则还包括循环展开和其他一些与处理器特性相关的优化工作。虽然优化选项可以加速代码的运行速度，但对于调试而言将是一个很大的挑战。因为代码在经过优化之后，原先在源程序中声明和使用的变量很可能不再使用，控制流也可能会突然跳转到意外的地方，循环语句也有可能因为循环展开而变得到处都有，所有这些对调试来讲都将是一场噩梦。所以笔者建议在调试的时候最好不使用任何优化选项，只有当程序在最终发行的时候才考虑对其进行优化。</div><div>（4）体系结构相关选项</div><div>Gcc的体系结构相关选项如表3.9 所示。</div><div>表3.9 Gcc体系结构相关选项列表</div><div>选 项 含 义</div><div>-mcpu=type 针对不同的CPU使用相应的CPU指令。可选择的type有i386、i486、pentium及i686等</div><div>-mieee-fp 使用IEEE标准进行浮点数的比较</div><div>-mno-ieee-fp 不使用IEEE标准进行浮点数的比较</div><div>-msoft-float 输出包含浮点库调用的目标代码</div><div>-mshort 把int类型作为16位处理，相当于short int</div><div>-mrtd 强行将函数参数个数固定的函数用ret NUM返回，节省调用函数的一条指令</div><div>这些体系结构相关选项在嵌入式的设计中会有较多的应用，读者需根据不同体系结构将</div><div>对应的选项进行组合处理。在本书后面涉及到具体实例会有针对性的讲解。</div></div><img src ="http://www.blogjava.net/jjshcc/aggbug/408978.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jjshcc/" target="_blank">Eric_jiang</a> 2014-01-15 15:21 <a href="http://www.blogjava.net/jjshcc/archive/2014/01/15/408978.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>