﻿<?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++博客-yezhangxiang</title><link>http://www.cppblog.com/yezhangxiang/</link><description /><language>zh-cn</language><lastBuildDate>Tue, 09 Jun 2026 20:27:54 GMT</lastBuildDate><pubDate>Tue, 09 Jun 2026 20:27:54 GMT</pubDate><ttl>60</ttl><item><title>[转]如何使用gcc编译器?</title><link>http://www.cppblog.com/yezhangxiang/archive/2010/10/23/131000.html</link><dc:creator>羊习习</dc:creator><author>羊习习</author><pubDate>Sat, 23 Oct 2010 13:15:00 GMT</pubDate><guid>http://www.cppblog.com/yezhangxiang/archive/2010/10/23/131000.html</guid><wfw:comment>http://www.cppblog.com/yezhangxiang/comments/131000.html</wfw:comment><comments>http://www.cppblog.com/yezhangxiang/archive/2010/10/23/131000.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/yezhangxiang/comments/commentRss/131000.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/yezhangxiang/services/trackbacks/131000.html</trackback:ping><description><![CDATA[
<span style="font-size: 14px; line-height: 18px; "><em>目录</em></span><span style="font-size: 14px; line-height: 18px; ">:</span><ul style="font-size: 14px; line-height: 18px; "><li><a href="http://linux.chinaunix.net/doc/2004-10-05/22.shtml#229lfindex0" style="color: rgb(0, 0, 255); ">GCC rules</a></li><li><a href="http://linux.chinaunix.net/doc/2004-10-05/22.shtml#229lfindex1" style="color: rgb(0, 0, 255); ">开始...</a></li><li><a href="http://linux.chinaunix.net/doc/2004-10-05/22.shtml#229lfindex2" style="color: rgb(0, 0, 255); ">预编译</a></li><li><a href="http://linux.chinaunix.net/doc/2004-10-05/22.shtml#229lfindex3" style="color: rgb(0, 0, 255); ">编译</a></li><li><a href="http://linux.chinaunix.net/doc/2004-10-05/22.shtml#229lfindex4" style="color: rgb(0, 0, 255); ">汇编</a></li><li><a href="http://linux.chinaunix.net/doc/2004-10-05/22.shtml#229lfindex5" style="color: rgb(0, 0, 255); ">连接</a></li><li><a href="http://linux.chinaunix.net/doc/2004-10-05/22.shtml#229lfindex6" style="color: rgb(0, 0, 255); ">另外两个重要选项</a></li><li><a href="http://linux.chinaunix.net/doc/2004-10-05/22.shtml#229lfindex7" style="color: rgb(0, 0, 255); ">调试</a></li><li><a href="http://linux.chinaunix.net/doc/2004-10-05/22.shtml#229lfindex8" style="color: rgb(0, 0, 255); ">小结</a></li><li><a href="http://linux.chinaunix.net/doc/2004-10-05/22.shtml#229lfindex9" style="color: rgb(0, 0, 255); ">站点链接</a></li></ul><span style="font-size: 14px; line-height: 18px; "><br></span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><p style="font-size: 14px; line-height: 18px; "><em>摘要</em>:</p><p style="font-size: 14px; line-height: 18px; ">要想读懂本文，你需要对C语言有基本的了解，本文将介绍如何使用gcc编译器。 首先，我们介绍如何在命令行方式下使用编译器编译简单的C源代码。 然后，我们简要介绍一下编译器究竟作了那些工作，以及如何控制编译过程。 我们也简要介绍了调试器的使用方法。&nbsp;<br><br><table width="300" align="center" border="0"><tbody><tr><td bgcolor="#8282e0" style="font-size: 12px; "><img src="http://linux.chinaunix.net/common/images/transpix.gif" width="1" height="2" alt=""></td></tr></tbody></table><a name="229lfindex0">&nbsp;</a></p><h2 style="font-size: 14px; line-height: 18px; ">GCC rules</h2><p style="font-size: 14px; line-height: 18px; ">你能想象使用封闭源代码的私有编译器编译自由软件吗？你怎么知道编译器在你的 可执行文件中加入了什么？可能会加入各种后门和木马。Ken Thompson是一个著名 的黑客，他编写了一个编译器，当编译器编译自己时，就在'login'程序中留下后门 和永久的木马。请到<a href="http://www.acm.org/classics/sep95" style="color: rgb(0, 0, 255); ">这里&nbsp;</a>阅读他对 这个杰作的描述。幸运的是，我们有了gcc。当你进行&nbsp;<code>configure; make; make install&nbsp;</code>时， gcc在幕后做了很多繁重的工作。如何才能让gcc为我们工作呢？我们将开始编写一个纸牌游戏， 不过我们只是为了演示编译器的功能，所以尽可能地精简了代码。 我们将从头开始一步一步地做，以便理解编译过程，了解为了制作可执行文件需要 做些什么，按什么顺序做。我们将看看如何编译C程序，以及如何使用编译选项 让gcc按照我们的要求工作。步骤（以及所用工具）如下：&nbsp;<a href="http://linux.chinaunix.net/doc/2004-10-05/22.shtml#precomp" style="color: rgb(0, 0, 255); ">预编译&nbsp;</a>(gcc -E)，&nbsp;<a href="http://linux.chinaunix.net/doc/2004-10-05/22.shtml#comp" style="color: rgb(0, 0, 255); ">编译&nbsp;</a>(gcc)，&nbsp;<a href="http://linux.chinaunix.net/doc/2004-10-05/22.shtml#assem" style="color: rgb(0, 0, 255); ">汇编</a>(as)，和&nbsp;<a href="http://linux.chinaunix.net/doc/2004-10-05/22.shtml#link" style="color: rgb(0, 0, 255); ">连接&nbsp;</a>(ld)。</p><span style="font-size: 14px; line-height: 18px; "><a name="229lfindex1">&nbsp;</a></span><h2 style="font-size: 14px; line-height: 18px; ">开始...</h2><p style="font-size: 14px; line-height: 18px; ">首先，我们应该知道如何调用编译器。实际上，这很简单。我们将从那个著名的第一个C程序开始。 （各位老前辈，请原谅我）。</p><p style="font-size: 14px; line-height: 18px; "></p><span style="font-size: 14px; line-height: 18px; "><pre>#include &lt;stdio.h&gt;

int main()<br>

{
  printf("Hello World!\n");
}
</pre></span><p style="font-size: 14px; line-height: 18px; "></p><p style="font-size: 14px; line-height: 18px; ">把这个文件保存为&nbsp;<code>game.c</code>。 你可以在命令行下编译它：</p><span style="font-size: 14px; line-height: 18px; "><pre>gcc game.c
</pre></span><span style="font-size: 14px; line-height: 18px; ">在默认情况下，C编译器将生成一个名为</span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; "><code>a.out</code></span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; ">的可执行文件。 你可以键入如下命令运行它：</span><span style="font-size: 14px; line-height: 18px; "><pre>a.out

<strong>Hello World</strong>

</pre></span><span style="font-size: 14px; line-height: 18px; ">每一次编译程序时，新的</span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; "><code>a.out</code></span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; ">将覆盖原来的程序。你无法知道是哪个 程序创建了</span><span style="font-size: 14px; line-height: 18px; "><code>a.out</code></span><span style="font-size: 14px; line-height: 18px; ">。我们可以通过使用</span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; "><code>-o</code></span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; ">编译选项，告诉 gcc我们想把可执行文件叫什么名字。我们将把这个程序叫做</span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; "><code>game</code></span><span style="font-size: 14px; line-height: 18px; ">，我们 可以使用任何名字，因为C没有Java那样的命名限制。</span><span style="font-size: 14px; line-height: 18px; "><pre>gcc -o game game.c

</pre></span><span style="font-size: 14px; line-height: 18px; "><pre>game
<strong>Hello World</strong>

</pre></span><p style="font-size: 14px; line-height: 18px; "></p><p style="font-size: 14px; line-height: 18px; ">到现在为止，我们离一个有用的程序还差得很远。如果你觉得沮丧，你可以想一想我们 已经编译并运行了一个程序。因为我们将一点一点为这个程序添加功能，所以我们必须 保证让它能够运行。似乎每个刚开始学编程的程序员都想一下子编一个1000行的程序， 然后一次修改所有的错误。没有人，我是说没有人，能做到这个。你应该先编一个可以 运行的小程序，修改它，然后再次让它运行。这可以限制你一次修改的错误数量。另外， 你知道刚才做了哪些修改使程序无法运行，因此你知道应该把注意力放在哪里。这可以 防止这样的情况出现：你认为你编写的东西应该能够工作，它也能通过编译，但它就是 不能运行。请切记，能够通过编译的程序并不意味着它是正确的。</p><p style="font-size: 14px; line-height: 18px; ">下一步为我们的游戏编写一个头文件。头文件把数据类型和函数声明集中到了一处。 这可以保证数据结构定义的一致性，以便程序的每一部分都能以同样的方式看待一切事情。</p><span style="font-size: 14px; line-height: 18px; "><pre>#ifndef DECK_H
#define DECK_H

#define DECKSIZE 52

typedef struct deck_t
{
  int card[DECKSIZE];
  /* number of cards used */
  int dealt;
}deck_t;

#endif /* DECK_H */
</pre></span><p style="font-size: 14px; line-height: 18px; ">把这个文件保存为&nbsp;<code>deck.h</code>。只能编译&nbsp;<code>.c</code>&nbsp;文件， 所以我们必须修改 game.c。在game.c的第2行，写上&nbsp;<code>#include "deck.h"</code>。 在第5行写上&nbsp;<code>deck_t deck;</code>。为了保证我们没有搞错，把它重新编译一次。</p><span style="font-size: 14px; line-height: 18px; "><pre>gcc -o game game.c
</pre></span><p style="font-size: 14px; line-height: 18px; "></p><p style="font-size: 14px; line-height: 18px; ">如果没有错误，就没有问题。如果编译不能通过，那么就修改它直到能通过为止。</p><span style="font-size: 14px; line-height: 18px; "><a name="229lfindex2">&nbsp;</a></span><h2 style="font-size: 14px; line-height: 18px; "><a name="precomp">预编译</a></h2><p style="font-size: 14px; line-height: 18px; ">编译器是怎么知道&nbsp;<code>deck_t</code>&nbsp;类型是什么的呢？因为在预编译期间， 它实际上把"deck.h"文件复制到了"game.c"文件中。源代码中的预编译指示以"#"为前缀。 你可以通过在gcc后加上&nbsp;<code>-E</code>&nbsp;选项来调用预编译器。</p><span style="font-size: 14px; line-height: 18px; "><pre>gcc -E -o game_precompile.txt game.c
wc -l game_precompile.txt
  3199 game_precompile.txt
</pre></span><span style="font-size: 14px; line-height: 18px; ">几乎有3200行的输出！其中大多数来自</span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; "><code>stdio.h</code></span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; ">包含文件，但是如果 你查看这个文件的话，我们的声明也在那里。如果你不用</span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; "><code>-o</code></span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; ">选项指定 输出文件名的话，它就输出到控制台。预编译过程通过完成三个主要任务给了代码很大的 灵活性。</span><ol style="font-size: 14px; line-height: 18px; "><li>把"include"的文件拷贝到要编译的源文件中。</li><li>用实际值替代"define"的文本。</li><li>在调用宏的地方进行宏替换。</li></ol><span style="font-size: 14px; line-height: 18px; ">这就使你能够在整个源文件中使用符号常量（即用DECKSIZE表示一付牌中的纸牌数量）， 而符号常量是在一个地方定义的，如果它的值发生了变化，所有使用符号常量的地方 都能自动更新。在实践中，你几乎不需要单独使用</span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; "><code>-E</code></span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; ">选项，而是让它 把输出传送给编译器。</span><p style="font-size: 14px; line-height: 18px; "></p><span style="font-size: 14px; line-height: 18px; "><a name="229lfindex3">&nbsp;</a></span><h2 style="font-size: 14px; line-height: 18px; "><a name="comp">编译</a></h2><p style="font-size: 14px; line-height: 18px; ">作为一个中间步骤，gcc把你的代码翻译成汇编语言。它一定要这样做，它必须通过分析 你的代码搞清楚你究竟想要做什么。如果你犯了语法错误，它就会告诉你，这样编译就失败了。 人们有时会把这一步误解为整个过程。但是，实际上还有许多工作要gcc去做呢。</p><span style="font-size: 14px; line-height: 18px; "><a name="229lfindex4">&nbsp;</a></span><h2 style="font-size: 14px; line-height: 18px; "><a name="assem">汇编</a></h2><p style="font-size: 14px; line-height: 18px; "><code>as</code>&nbsp;把汇编语言代码转换为目标代码。事实上目标代码并不能在CPU上运行， 但它离完成已经很近了。编译器选项&nbsp;<code>-c</code>&nbsp;把 .c 文件转换为以 .o 为扩展名 的目标文件。 如果我们运行</p><span style="font-size: 14px; line-height: 18px; "><pre>gcc -c game.c
</pre></span><span style="font-size: 14px; line-height: 18px; ">我们就自动创建了一个名为game.o的文件。这里我们碰到了一个重要的问题。我们可以用 任意一个 .c 文件创建一个目标文件。正如我们在下面所看到的，在连接步骤中我们可以 把这些目标文件组合成可执行文件。让我们继续介绍我们的例子。因为我们正在编写一个 纸牌游戏，我们已经把一付牌定义为</span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; "><code>deck_t</code></span><span style="font-size: 14px; line-height: 18px; ">，我们将编写一个洗牌函数。 这个函数接受一个指向deck类型的指针，并把一付随机的牌装入deck类型。它使用'drawn' 数组跟踪记录那些牌已经用过了。这个具有DECKSIZE个元素的数组可以防止我们重复使用 一张牌。</span><p style="font-size: 14px; line-height: 18px; "></p><span style="font-size: 14px; line-height: 18px; "><pre>#include &lt;stdlib.h&gt;
#include &lt;stdio.h&gt;
#include &lt;time.h&gt;
#include "deck.h"

static time_t seed = 0;

void shuffle(deck_t *pdeck)
{
  /* Keeps track of what numbers have been used */
  int drawn[DECKSIZE] = {0};
  int i;

  /* One time initialization of rand */
  if(0 == seed)
  {
    seed = time(NULL);
    srand(seed);
  }
  for(i = 0; i &lt; DECKSIZE; i++)
  {
    int value = -1;
    do
    {
      value = rand() % DECKSIZE;
    }
    while(drawn[value] != 0);

    /* mark value as used */
    drawn[value] = 1;

    /* debug statement */
    printf("%i\n", value);
    pdeck-&gt;card[i] = value;
  }
  pdeck-&gt;dealt = 0;
  return;
}
</pre></span><p style="font-size: 14px; line-height: 18px; ">把这个文件保存为&nbsp;<code>shuffle.c</code>。我们在这个代码中加入了一条调试语句， 以便运行时，能输出所产生的牌号。这并没有为我们的程序添加功能，但是现在到了 关键时刻，我们看看究竟发生了什么。因为我们的游戏还在初级阶段，我们没有别的 办法确定我们的函数是否实现了我们要求的功能。使用那条printf语句，我们就能准确 地知道现在究竟发生了什么，以便在开始下一阶段之前我们知道牌已经洗好了。在我们 对它的工作感到满意之后，我们可以把那一行语句从代码中删掉。这种调试程序的技术 看起来很粗糙，但它使用最少的语句完成了调试任务。以后我们再介绍更复杂的调试器。</p><span style="font-size: 14px; line-height: 18px; ">请注意两个问题。</span><ol style="font-size: 14px; line-height: 18px; "><li>我们用传址方式传递参数，你可以从'&amp;'（取地址）操作符看出来。这把变量的机器地址 传递给了函数，因此函数自己就能改变变量的值。也可以使用全局变量编写程序，但是应该 尽量少使用全局变量。指针是C的一个重要组成部分，你应该充分地理解它。</li><li>我们在一个新的 .c 文件中使用函数调用。操作系统总是寻找名为'main'的函数，并从 那里开始执行。&nbsp;<code>shuffle.c</code>&nbsp;中没有'main'函数，因此不能编译为独立的可执行文件。 我们必须把它与另一个具有'main'函数并调用'shuffle'的程序组合起来。</li></ol><p style="font-size: 14px; line-height: 18px; ">运行命令</p><span style="font-size: 14px; line-height: 18px; "><pre>gcc -c shuffle.c
</pre></span><span style="font-size: 14px; line-height: 18px; ">并确定它创建了一个名为</span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; "><code>shuffle.o</code></span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; ">的新文件。编辑game.c文件，在第7行，在 deck_t类型的变量</span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; "><code>deck</code></span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; ">声明之后，加上下面这一行：</span><span style="font-size: 14px; line-height: 18px; "><pre>shuffle(&amp;deck);
</pre></span><span style="font-size: 14px; line-height: 18px; ">现在，如果我们还象以前一样创建可执行文件，我们就会得到一个错误</span><span style="font-size: 14px; line-height: 18px; "><pre>gcc -o game game.c

/tmp/ccmiHnJX.o: In function `main':
/tmp/ccmiHnJX.o(.text+0xf): undefined reference to `shuffle'
collect2: ld returned 1 exit status
</pre></span><span style="font-size: 14px; line-height: 18px; ">编译成功了，因为我们的语法是正确的。但是连接步骤却失败了，因为 我们没有告诉编译器'shuffle'函数在哪里。 那么，到底什么是连接？我们怎样告诉编译器到哪里寻找这个函数呢？</span><p style="font-size: 14px; line-height: 18px; "></p><span style="font-size: 14px; line-height: 18px; "><a name="229lfindex5">&nbsp;</a></span><h2 style="font-size: 14px; line-height: 18px; "><a name="link">连接</a></h2><p style="font-size: 14px; line-height: 18px; ">连接器<code>ld</code>，使用下面的命令，接受前面由&nbsp;<code>as</code>&nbsp;创建的目标文件并把它转换为可执行文件</p><span style="font-size: 14px; line-height: 18px; "><pre>gcc -o game game.o shuffle.o
</pre></span><span style="font-size: 14px; line-height: 18px; ">这将把两个目标文件组合起来并创建可执行文件</span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; "><code>game</code></span><span style="font-size: 14px; line-height: 18px; ">。</span><p style="font-size: 14px; line-height: 18px; "></p><p style="font-size: 14px; line-height: 18px; ">连接器从shuffle.o目标文件中找到&nbsp;<code>shuffle</code>&nbsp;函数，并把它包括进可执行文件。 目标文件的真正好处在于，如果我们想再次使用那个函数，我们所要做的就是包含"deck.h" 文件并把&nbsp;<code>shuffle.o</code>&nbsp;目标文件连接到新的可执行文件中。</p><p style="font-size: 14px; line-height: 18px; ">象这样的代码重用是经常发生的。虽然我们并没有编写前面作为调试语句调用的&nbsp;<code>printf</code>&nbsp;函数，连接器却能从我们用&nbsp;<code>#include &lt;stdlib.h&gt;</code>&nbsp;语句包含的文件中 找到它的声明，并把存储在C库（/lib/libc.so.6）中的目标代码连接进来。 这种方式使我们可以使用已能正确工作的其他人的函数，只关心我们所要解决的问题。 这就是为什么头文件中一般只含有数据和函数声明，而没有函数体。一般，你可以为 连接器创建目标文件或函数库，以便连接进可执行文件。我们的代码可能产生问题，因为 在头文件中我们没有放入任何函数声明。为了确保一切顺利，我们还能做什么呢？</p><span style="font-size: 14px; line-height: 18px; "><a name="229lfindex6">&nbsp;</a></span><h2 style="font-size: 14px; line-height: 18px; "><a name="lasttwo">另外两个重要选项</a></h2><p style="font-size: 14px; line-height: 18px; "><code>-Wall</code>&nbsp;选项可以打开所有类型的语法警告，以便帮助我们确定代码是正确的， 并且尽可能实现可移植性。当我们使用这个选项编译我们的代码时，我们将看到下述警告：</p><span style="font-size: 14px; line-height: 18px; "><pre>game.c:9: warning: implicit declaration of function `shuffle'
</pre></span><span style="font-size: 14px; line-height: 18px; ">这让我们知道还有一些工作要做。我们需要在头文件中加入一行代码，以便告诉编译器有关</span><span style="font-size: 14px; line-height: 18px; "><code>shuffle</code></span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; ">函数的一切，让它可以做必要的检查。听起来象是一种狡辩，但这样做 可以把函数的定义与实现分离开来，使我们能在任何地方使用我们的函数，只要包含新的头文件 并把它连接到我们的目标文件中就可以了。下面我们就把这一行加入deck.h中。</span><span style="font-size: 14px; line-height: 18px; "><pre>void shuffle(deck_t *pdeck);
</pre></span><span style="font-size: 14px; line-height: 18px; ">这就可以消除那个警告信息了。</span><p style="font-size: 14px; line-height: 18px; "></p><p style="font-size: 14px; line-height: 18px; ">另一个常用编译器选项是优化选项&nbsp;<code>-O#</code>&nbsp;(即 -O2)。 这是告诉编译器你需要什么级别的优化。编译器具有一整套技巧可以使你的代码运行得更快一点。 对于象我们这种小程序，你可能注意不到差别，但对于大型程序来说，它可以大幅度提高运行速度。 你会经常碰到它，所以你应该知道它的意思。</p><span style="font-size: 14px; line-height: 18px; "><a name="229lfindex7">&nbsp;</a></span><h2 style="font-size: 14px; line-height: 18px; "><a name="debug">调试</a></h2><p style="font-size: 14px; line-height: 18px; ">我们都知道，代码通过了编译并不意味着它按我们得要求工作了。你可以使用下面的命令验证 是否所有的号码都被使用了</p><span style="font-size: 14px; line-height: 18px; "><pre>game | sort - n | less
</pre></span><span style="font-size: 14px; line-height: 18px; ">并且检查有没有遗漏。如果有问题我们该怎么办？我们如何才能深入底层查找错误呢？</span><p style="font-size: 14px; line-height: 18px; "></p><span style="font-size: 14px; line-height: 18px; ">你可以使用调试器检查你的代码。大多数发行版都提供著名的调试器：gdb。如果那些众多的命令行选项 让你感到无所适从，那么你可以使用KDE提供的一个很好的前端工具</span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; "><a href="http://members.nextra.at/johsixt/kdbg.html" style="color: rgb(0, 0, 255); ">KDbg</a></span><span style="font-size: 14px; line-height: 18px; ">。 还有一些其它的前端工具，它们都很相似。要开始调试，你可以选择 File-&gt;Executable 然后找到你的</span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; "><code>game</code></span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; ">程序。 当你按下F5键或选择 Execution-&gt;从菜单运行时，你可以在另一个窗口中看到输出。 怎么回事？在那个窗口中我们什么也看不到。不要担心，KDbg没有出问题。问题在于我们 在可执行文件中没有加入任何调试信息，所以KDbg不能告诉我们内部发生了什么。编译器选项</span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; "><code>-g</code></span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; ">可以把必要的调试信息加入目标文件。你必须用这个选项编译目标文件 （扩展名为.o），所以命令行成了：</span><span style="font-size: 14px; line-height: 18px; "><pre>gcc -g -c shuffle.c game.c
gcc -g -o game game.o shuffle.o
</pre></span><span style="font-size: 14px; line-height: 18px; ">这就把钩子放入了可执行文件，使gdb和KDbg能指出运行情况。调试是一种很重要的技术，很 值得你花时间学习如何使用。调试器帮助程序员的方法是它能在源代码中设置&#8220;断点&#8221;。现在你可以 用右键单击调用</span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; "><code>shuffle</code></span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; ">函数的那行代码，试着设置断点。那一行边上会出现一个 红色的小圆圈。现在当你按下F5键时，程序就会在那一行停止执行。按F8可以跳入shuffle函数。 呵，我们现在可以看到</span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; "><code>shuffle.c</code></span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; ">中的代码了！我们可以控制程序一步一步地执行， 并看到究竟发生了什么事。如果你把光标暂停在局部变量上，你将能看到变量的内容。 太好了。这比那条</span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; "><code>printf</code></span><span style="font-size: 14px; line-height: 18px; ">&nbsp;</span><span style="font-size: 14px; line-height: 18px; ">语句好多了，是不是？</span><p style="font-size: 14px; line-height: 18px; "></p><span style="font-size: 14px; line-height: 18px; "><a name="229lfindex8">&nbsp;</a></span><h2 style="font-size: 14px; line-height: 18px; ">小结</h2><p style="font-size: 14px; line-height: 18px; ">本文大体介绍了编译和调试C程序的方法。我们讨论了编译器走过的步骤，以及为了让 编译器做这些工作应该给gcc传递哪些选项。我们简述了有关连接共享函数库的问题， 最后介绍了调试器。真正了解你所从事的工作还需要付出许多努力，但我希望本文 能让你正确地起步。你可以在&nbsp;<code>gcc</code>、&nbsp;<code>as</code>&nbsp;和&nbsp;<code>ld</code>的&nbsp;<code>man</code>&nbsp;和&nbsp;<code>info</code>&nbsp;page中 找到更多的信息。</p><p style="font-size: 14px; line-height: 18px; ">自己编写代码可以让你学到更多的东西。作为练习你可以以本文的纸牌游戏为基础，编写 一个21点游戏。那时你可以学学如何使用调试器。使用GUI的KDbg开始可以更容易一些。 如果你每次只加入一点点功能，那么很快就能完成。切记，一定要保持程序一直能运行！</p><p style="font-size: 14px; line-height: 18px; ">要想编写一个完整的游戏，你需要下面这些内容：</p><ul style="font-size: 14px; line-height: 18px; "><li>一个纸牌玩家的定义（即，你可以把deck_t定义为player_t）。</li><li>一个给指定玩家发一定数量牌的函数。记住在纸牌中要增加&#8220;已发牌&#8221;的数量，以便 能知道还有那些牌可发。还要记住玩家手中还有多少牌。</li><li>一些与用户的交互，问问玩家是否还要另一张牌。</li><li>一个能打印玩家手中的牌的函数。&nbsp;<em>card</em>&nbsp;等于value % 13 （得数为0到12），<em>suit</em>等于 value / 13 （得数为0到3）。</li><li>一个能确定玩家手中的value的函数。Ace的value为零并且可以等于1或11。King的value为12并且可以等于10。</li></ul><p style="font-size: 14px; line-height: 18px; "></p><p style="font-size: 14px; line-height: 18px; "></p><span style="font-size: 14px; line-height: 18px; "><a name="229lfindex9">&nbsp;</a></span><h2 style="font-size: 14px; line-height: 18px; "><a name="links">站点链接</a></h2><ul style="font-size: 14px; line-height: 18px; "><li><a href="http://www.gnu.org/directory/gcc.html" style="color: rgb(0, 0, 255); ">gcc</a>&nbsp;GCC GNU Compiler Collection</li><li><a href="http://www.gnu.org/directory/gdb.html" style="color: rgb(0, 0, 255); ">gdb</a>&nbsp;GNU Debugger</li><li><a href="http://members.nextra.at/johsixt/kdbg.html" style="color: rgb(0, 0, 255); ">KDbg</a>&nbsp;KDE's GUI Debugger</li><li><a href="http://www.acm.org/classics/sep95" style="color: rgb(0, 0, 255); ">Award Winning Compiler Hack</a>&nbsp;Ken Thompson's great compiler hack</li><li></li></ul><img src ="http://www.cppblog.com/yezhangxiang/aggbug/131000.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/yezhangxiang/" target="_blank">羊习习</a> 2010-10-23 21:15 <a href="http://www.cppblog.com/yezhangxiang/archive/2010/10/23/131000.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>