gcc编译器使用说明_第1页
gcc编译器使用说明_第2页
gcc编译器使用说明_第3页
gcc编译器使用说明_第4页
gcc编译器使用说明_第5页
已阅读5页,还剩4页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、要想读懂本文,你需要对 C语言有基本的了解,本文将介绍如何使用gcc 编译器。首先,我们介绍如何在命令行方式下使用编译器编译简单的C源代 码。然后,我们简要介绍一下编译器究竟作了那些工作,以及如何控制编译过 程。我们也简要介绍了调试器的使用方法。 GCC rules 你能想象使用封闭源代码的私有编译器编译自由软件吗?你怎么知道编译 器在你的可执行文件中加入了什么?可能会加入各种后门和木马。 Ken Thompson是一个著名的黑客,他编写了一个编译器,当编译器编译自己 时,就在login程序中留下后门和永久的木马。请到这里阅读他对这个杰作的描 述。幸运的是,我们有了 gcco 当你进行 con

2、 figure;make;make in stall时,gcc在 幕后做了很多繁重的工作。如何才能让gcc为我们工作呢?我们将开始编写一 个纸牌游戏,不过我们只是为了演示编译器的功能,所以尽可能地精简了代 码。我们将从头开始一步地做,以便理解编译过程,了解为了制作可执行文件 需要做些什么,按什么顺序做。我们将看看如何编译C程序,以及如何使用编 译选项让gcc按照我们的要求工作。步骤(以及所用工具)如下: 预编译(gcc -E)编译(gcc),汇编(as),和连接(Id)。 开始 . 首先,我们应该知道如何调用编译器。实际上,这很简单。我们将从那个 著名的第一个C程序开始。(各位老前辈,请原谅我

3、)。 #include int main()printf(Hello World!n);把这个文件保存为 game.c。你可以在命令 行下编译它: gcc game.c 在默认情况下,C编译器将生成一个名为a.out的可执行文件。你可以键入 如下命令运行它: a.out Hello World 每一次编译程序时,新的 a.out 将覆盖原来的程序。你无法知道是哪个程序 创建了 a.out。我们可以通过使用-o编译选项,告诉gcc我们想把可执行文件叫 什么名字。我们将把这个程序叫做 game,我们可以使用任何名字,因为 C没有 Java那样的命名限制。 gcc -o game game.c ga

4、me Hello World 到现在为止,我们离一个有用的程序还差得很远。如果你觉得沮丧,你可 以想一想我们已经编译并运行了一个程序。因为我们将一点为这个程序添加功 能,所以我们必须保证让它能够运行。似乎每个刚开始学编程的程序员都想一 下子编一个 1000 行的程序,然后一次修改所有的错误。没有人,我是说没有 人,能做到这个。你应该先编一个可以运行的小程序,修改它,然后再次让它 运行。这可以限制你一次修改的错误数量。另外,你知道刚才做了哪些修改使 程序无法运行,因此你知道应该把注意力放在哪里。这可以防止这样的情况出 现: 你认为你编写的东西应该能够工作,它也能通过编译,但它就是不能运 行。请切

5、记,能够通过编译的程序并不意味着它是正确的。 下一步为我们的游戏编写一个头文件。头文件把数据类型和函数声明集中 到了一处。这可以保证数据结构定义的一致性,以便程序的每一部分都能以同 样的方式看待一切事情。 #ifndef DECK_H #define DECK_H #define DECKSIZE 52 typedef struct deck_tint cardDECKSIZE; /* number of cards used */ int dealt; deck_t; #endif /* DECK_H */ 把这个文件保存为deck.h。只能编译.c文件,所以我们必须修改game.c。 在g

6、ame.c的第2行,写上#includedeck.h。在第5行写上deck_tdeck;。为了保 证我们没有搞错,把它重新编译一次。 gcc -o game game.c 如果没有错误,就没有问题。如果编译不能通过,那么就修改它直到能通 过为止。预编译 编译器是怎么知道deck_t类型是什么的呢?因为在预编译期间,它实际上 把deck.h文件复制到了 game.c文件中。源代码中的预编译指示以#为前缀。 你可以通过在gcc后加上-E选项来调用预编译器。 几乎有 3200 行的输出!其中大多数来自 stdio.h 包含文件,但是如果你查 看这个文件的话,我们的声明也在那里。如果你不用-o选项指定

7、输出文件名的 话,它就输出到控制台。预编译过程通过完成三个主要任务给了代码很大的灵 活性。 把in clude的文件拷贝到要编译的源文件中。 用实际值替代define的文本。 在调用XX的地方进行XX替换。 这就使你能够在整个源文件中使用符号常量(即用DECKSIZ表示一付牌中 的纸牌数量),而符号常量是在一个地方定义的,如果它的值发生了变化,所 有使用符号常量的地方都能自动更新。在实践中,你几乎不需要单独使用-E选 项,而是让它把输出传送给编译器。 编译 作为一个中间步骤, gcc 把你的代码翻译成汇编语言。它一定要这样做,它 必须通过分析你的代码搞清楚你究竟想要做什么。如果你犯了语法错误,

8、它就 会告诉你,这样编译就失败了。 人们有时会把这一步误解为整个过程。但是,实际上还有许多工作要 gcc 去做呢。 汇编 as把汇编语言代码转换为目标代码。事实上目标代码并不能在 CPU上运 行,但它离完成已经很近了。编译器选项-c把.C文件转换为以.0为扩展名的目 标文件。如果我们运行 gcc -c game.c 我们就自动创建了一个名为game.o的文件。这里我们碰到了一个重要的问 题。我们可以用任意一个.c文件创建一个目标文件。正如我们在下面所看到 的,在连接步骤中我们可以把这些目标文件组合成可执行文件。让我们继续介 绍我们的例子。因为我们正在编写一个纸牌游戏,我们已经把一付牌定义为 d

9、eck_t,我们将编写一个洗牌函数。这个函数接受一个指向deck类型的指针, 并把一付随机的牌装入deck类型。它使用drawn数组跟踪记录那些牌已经用过 了。这个具有DECKSIZ个元素的数组可以防止我们重复使用一张牌。 #include #include #include #include deck.h static time_t seed = 0; void shuffle(deck_t *pdeck)/* Keeps track of what numbers have been used */ int drawnDECKSIZE = 0; int i; /* One time ini

10、tialization of rand */ if(0 = seed)seed = time(NULL); srand(seed);for(i = 0; i cardi = value;pdeck-dealt = 0; return;把这个文件保存为shuffle.c。我们在这个代码中加入了一条调试语 句,以便运行时,能输出所产生的牌号。这并没有为我们的程序添加功能,但 是现在到了关键时刻,我们看看究竟发生了什么。因为我们的游戏还在初级阶 段,我们没有别的办法确定我们的函数是否实现了我们要求的功能。使用那条 printf 语句,我们就能准确地知道现在究竟发生了什么,以便在开始下一阶段之 前我们

11、知道牌已经洗好了。在我们对它的工作感到满意之后,我们可以把那一 行语句从代码中删掉。这种调试程序的技术看起来很粗糙,但它使用最少的语 句完成了调试任务。以后我们再介绍更复杂的调试器。请注意两个问题。 我们用传址方式传递参数,你可以从 现在,如果我们还象以前一样创建可执行文件,我们就会得到一个错误 gcc -o game game.c /tmp/ccmiHnJX.o: In fun cti on ma in: /tmp/ccmiHnJX.o(.text+0 xf): undefined reference to shuffle collect2: ld returned 1 exit statu

12、s 编译成功了,因为我们的语法是正确的。但是连接步骤却失败了,因为我 们没有告诉编译器shuffle函数在哪里。那么,到底什么是连接?我们怎样告诉 编译器到哪里寻找这个函数呢? 连接 连接器Id,使用下面的命令,接受前面由as创建的目标文件并把它转换为 可执行文件 gcc -o game game.o shuffle.o 这将把两个目标文件组合起来并创建可执行文件 game。 连接器从 shuffle.o 目标文件中找到 shuffle 函数,并把它包括进可执行文 件。目标文件的真正好处在于,如果我们想再次使用那个函数,我们所要做的 就是包含deck.h文件并把shuffle.o目标文件连接到

13、新的可执行文件中。 象这样的代码重用是经常发生的。虽然我们并没有编写前面作为调试语句 调用的printf函数,连接器却能从我们用#include语句包含的文件中找 到它的声明,并把存储在 C库(/lib/libc.so.6 )中的目标代码连接进来。这种方 式使我们可以使用已能正确工作的其他人的函数,只关心我们所要解决的问 题。这就是为什么头文件中一般只含有数据和函数声明,而没有函数体。一 般,你可以为连接器创建目标文件或函数库,以便连接进可执行文件。我们的 代码可能产生问题,因为在头文件中我们没有放入任何函数声明。为了确保一 切顺利,我们还能做什么呢? 另外两个重要选项 -Wall 选项可以打

14、开所有类型的语法警告,以便帮助我们确定代码是正确 的,并且尽可能实现可移植性。当我们使用这个选项编译我们的代码时,我们 将看到下述警告: game.c:9: warning: implicit declarati on of fun cti on shuffle 这让我们知道还有一些工作要做。我们需要在头文件中加入一行代码,以 便告诉编译器有关 shuffle 函数的一切,让它可以做必要的检查。听起来象是一 种狡辩,但这样做可以把函数的定义与实现分离开来,使我们能在任何地方使 用我们的函数,只要包含新的头文件并把它连接到我们的目标文件中就可以 了。下面我们就把这一行加入 deck.h 中。 v

15、oid shuffle(deck_t *pdeck); 这就可以消除那个警告信息了。 另一个常用编译器选项是优化选项-0#(即-02)。这是告诉编译器你需要什么 级别的优化。 编译器具有一整套技巧可以使你的代码运行得更快一点。对于象我们这种 小程序,你可能注意不到差别,但对于大型程序来说,它可以大幅度提高运行 速度。你会经常碰到它,所以你应该知道它的意思。 调试 我们都知道,代码通过了编译并不意味着它按我们得要求工作了。你可以 使用下面的命令验证是否所有的号码都被使用了 game | sort - n | less 并且检查有没有遗漏。如果有问题我们该怎么办?我们如何才能深入底层 查找错误呢?

16、你可以使用调试器检查你的代码。大多数发行版都提供著名的调 试器: gdb。如果那些众多的命令行选项让你感到无所适从,那么你可以使用KDE 提供的一个很好的前端工具 KDbgo还有一些其它的前端工具,它们都很相似。 要开始调试,你可以选择File-Executable然后找到你的game程序。当你按下 F5键或选择Execution-从菜单运行时,你可以在另一个窗口中看到输出。怎么 回事?在那个窗口中我们什么也看不到。不要担心,KDbg没有出问题。问题在 于我们在可执行文件中没有加入任何调试信息,所以KDbg不能告诉我们内部发 生了什么。 编译器选项 -g 可以把必要的调试信息加入目标文件。你必

17、须用这个选项编 译目标文件(扩展名为.0),所以命令行成了: gcc -g -c shuffle.c game.c gcc -g -o game game.o shuffle.o 这就把钩子放入了可执行文件,使 gdb和KDbg能指出运行情况。调试是一 种很重要的技术,很值得你花时间学习如何使用。调试器帮助程序员的方法是 它能在源代码中设置 断点”现在你可以用右键单击调用 shuffle函数的那行代 码,试着设置断点。那一行边上会出现一个红色的小圆圈。现在当你按下 F5键 时,程序就会在那一行停止执行。按 F8可以跳入shuffle函数。呵,我们现在可 以看到shuffle.c中的代码了!我们

18、可以控制程序一步地执行,并看到究竟发生 了什么事。如果你把光标暂停在局部变量上,你将能看到变量的内容。太好 了。这比那条 printf 语句好多了,是不是? 小结 本文大体介绍了编译和调试 C程序的方法。我们讨论了编译器走过的步 骤,以及为了让编译器做这些工作应该给gcc传递哪些选项。我们简述了有关 连接共享函数库的问题,最后介绍了调试器。真正了解你所从事的工作还需要 付出许多努力,但我希望本文能让你正确地起步。 你可以在gcc、as和Id的man和info page中找到更多的信息。 自己编写代码可以让你学到更多的东西。作为练习你可以以本文的纸牌游 戏为基础,编写一个21点游戏。那时你可以学学如何使用调试器。使用GUI的 KDbg开始可以更容易一些。如果你每次只加入一点点功能,那么很快就能完 成。切记,一定要保持程序一直能运行!要想编

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论