ImageVerifierCode 换一换
格式:DOCX , 页数:18 ,大小:32.85KB ,
资源ID:5465785      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/5465785.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(LinuxGCCC语言编辑器.docx)为本站会员(b****4)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

LinuxGCCC语言编辑器.docx

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

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

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

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

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

6、c的第2行,写上 #include deck.h。在第5行写上 deck_t deck;。为了保证我们没有搞错,把它重新编译一次。 gcc -o game game.c如果没有错误,就没有问题。如果编译不能通过,那么就修改它直到能通过为止。 预编译 编译器是怎么知道 deck_t 类型是什么的呢?因为在预编译期间,它实际上把deck.h文件复制到了game.c文件中。源代码中的预编译指示以#为前缀。你可以通过在gcc后加上 -E 选项来调用预编译器。 gcc -E -o game_precompile.txt game.cwc -l game_precompile.txt 3199 game_

7、precompile.txt几乎有3200行的输出!其中大多数来自 stdio.h 包含文件,但是如果你查看这个文件的话,我们的声明也在那里。如果你不用 -o 选项指定输出文件名的话,它就输出到控制台。预编译过程通过完成三个主要任务给了代码很大的灵活性。 1. 把include的文件拷贝到要编译的源文件中。 2. 用实际值替代define的文本。 3. 在调用宏的地方进行宏替换。 这就使你能够在整个源文件中使用符号常量(即用DECKSIZE表示一付牌中的纸牌数量),而符号常量是在一个地方定义的,如果它的值发生了变化,所有使用符号常量的地方都能自动更新。在实践中,你几乎不需要单独使用 -E 选项

8、,而是让它把输出传送给编译器。 编译 作为一个中间步骤,gcc把你的代码翻译成汇编语言。它一定要这样做,它必须通过分析你的代码搞清楚你究竟想要做什么。如果你犯了语法错误,它就会告诉你,这样编译就失败了。人们有时会把这一步误解为整个过程。但是,实际上还有许多工作要gcc去做呢。 汇编 as 把汇编语言代码转换为目标代码。事实上目标代码并不能在CPU上运行,但它离完成已经很近了。编译器选项 -c 把 .c 文件转换为以 .o 为扩展名的目标文件。 如果我们运行 gcc -c game.c我们就自动创建了一个名为game.o的文件。这里我们碰到了一个重要的问题。我们可以用任意一个 .c 文件创建一个

9、目标文件。正如我们在下面所看到的,在连接步骤中我们可以把这些目标文件组合成可执行文件。让我们继续介绍我们的例子。因为我们正在编写一个纸牌游戏,我们已经把一付牌定义为 deck_t,我们将编写一个洗牌函数。这个函数接受一个指向deck类型的指针,并把一付随机的牌装入deck类型。它使用drawn 数组跟踪记录那些牌已经用过了。这个具有DECKSIZE个元素的数组可以防止我们重复使用一张牌。 #include #include #include #include deck.hstatic time_t seed = 0;void shuffle(deck_t *pdeck) /* Keeps tr

10、ack of what numbers have been used */ int drawnDECKSIZE = 0; int i; /* One time initialization of rand */ if(0 = seed) seed = time(NULL); srand(seed); for(i = 0; i cardi = value; pdeck-dealt = 0; return;把这个文件保存为 shuffle.c。我们在这个代码中加入了一条调试语句,以便运行时,能输出所产生的牌号。这并没有为我们的程序添加功能,但是现在到了关键时刻,我们看看究竟发生了什么。因为我们的游

11、戏还在初级阶段,我们没有别的办法确定我们的函数是否实现了我们要求的功能。使用那条printf语句,我们就能准确地知道现在究竟发生了什么,以便在开始下一阶段之前我们知道牌已经洗好了。在我们对它的工作感到满意之后,我们可以把那一行语句从代码中删掉。这种调试程序的技术看起来很粗糙,但它使用最少的语句完成了调试任务。以后我们再介绍更复杂的调试器。 请注意两个问题。 1. 我们用传址方式传递参数,你可以从&(取地址)操作符看出来。这把变量的机器地址传递给了函数,因此函数自己就能改变变量的值。也可以使用全局变量编写程序,但是应该尽量少使用全局变量。指针是C的一个重要组成部分,你应该充分地理解它。 2. 我

12、们在一个新的 .c 文件中使用函数调用。操作系统总是寻找名为main的函数,并从那里开始执行。 shuffle.c 中没有main函数,因此不能编译为独立的可执行文件。我们必须把它与另一个具有main函数并调用shuffle的程序组合起来。 运行命令 gcc -c shuffle.c并确定它创建了一个名为 shuffle.o 的新文件。编辑game.c文件,在第7行,在 deck_t类型的变量 deck 声明之后,加上下面这一行: shuffle(&deck);现在,如果我们还象以前一样创建可执行文件,我们就会得到一个错误 gcc -o game game.c/tmp/ccmiHnJX.o:

13、In function main:/tmp/ccmiHnJX.o(.text+0xf): undefined reference to shufflecollect2: ld returned 1 exit status编译成功了,因为我们的语法是正确的。但是连接步骤却失败了,因为我们没有告诉编译器shuffle函数在哪里。那么,到底什么是连接?我们怎样告诉编译器到哪里寻找这个函数呢? 连接 连接器ld,使用下面的命令,接受前面由 as 创建的目标文件并把它转换为可执行文件 gcc -o game game.o shuffle.o这将把两个目标文件组合起来并创建可执行文件 game。 连接器从

14、shuffle.o目标文件中找到 shuffle 函数,并把它包括进可执行文件。目标文件的真正好处在于,如果我们想再次使用那个函数,我们所要做的就是包含deck.h 文件并把 shuffle.o 目标文件连接到新的可执行文件中。 象这样的代码重用是经常发生的。虽然我们并没有编写前面作为调试语句调用的 printf 函数,连接器却能从我们用 #include 语句包含的文件中找到它的声明,并把存储在C库(/lib/libc.so.6)中的目标代码连接进来。这种方式使我们可以使用已能正确工作的其他人的函数,只关心我们所要解决的问题。这就是为什么头文件中一般只含有数据和函数声明,而没有函数体。一般,

15、你可以为连接器创建目标文件或函数库,以便连接进可执行文件。我们的代码可能产生问题,因为在头文件中我们没有放入任何函数声明。为了确保一切顺利,我们还能做什么呢? 另外两个重要选项 -Wall 选项可以打开所有类型的语法警告,以便帮助我们确定代码是正确的,并且尽可能实现可移植性。当我们使用这个选项编译我们的代码时,我们将看到下述警告: game.c:9: warning: implicit declaration of function shuffle这让我们知道还有一些工作要做。我们需要在头文件中加入一行代码,以便告诉编译器有关 shuffle 函数的一切,让它可以做必要的检查。听起来象是一种狡

16、辩,但这样做 可以把函数的定义与实现分离开来,使我们能在任何地方使用我们的函数,只要包含新的头文件 并把它连接到我们的目标文件中就可以了。下面我们就把这一行加入deck.h中。 void shuffle(deck_t *pdeck);这就可以消除那个警告信息了。 另一个常用编译器选项是优化选项 -O# (即 -O2)。 这是告诉编译器你需要什么级别的优化。编译器具有一整套技巧可以使你的代码运行得更快一点。对于象我们这种小程序,你可能注意不到差别,但对于大型程序来说,它可以大幅度提高运行速度。你会经常碰到它,所以你应该知道它的意思。 调试 我们都知道,代码通过了编译并不意味着它按我们得要求工作了

17、。你可以使用下面的命令验证是否所有的号码都被使用了 game | sort - n | less并且检查有没有遗漏。如果有问题我们该怎么办?我们如何才能深入底层查找错误呢? 你可以使用调试器检查你的代码。大多数发行版都提供著名的调试器:gdb。如果那些众多的命令行选项让你感到无所适从,那么你可以使用KDE提供的一个很好的前端工具 KDbg。还有一些其它的前端工具,它们都很相似。要开始调试,你可以选择 File-Executable 然后找到你的 game 程序。当你按下F5键或选择 Execution-从菜单运行时,你可以在另一个窗口中看到输出。怎么回事?在那个窗口中我们什么也看不到。不要担心

18、,KDbg没有出问题。问题在于我们在可执行文件中没有加入任何调试信息,所以KDbg不能告诉我们内部发生了什么。编译器选项 -g 可以把必要的调试信息加入目标文件。你必须用这个选项编译目标文件(扩展名为.o),所以命令行成了: gcc -g -c shuffle.c game.cgcc -g -o game game.o shuffle.o这就把钩子放入了可执行文件,使gdb和KDbg能指出运行情况。调试是一种很重要的技术,很值得你花时间学习如何使用。调试器帮助程序员的方法是它能在源代码中设置“断点”。现在你可以用右键单击调用 shuffle 函数的那行代码,试着设置断点。那一行边上会出现一个红

19、色的小圆圈。现在当你按下F5键时,程序就会在那一行停止执行。按F8可以跳入shuffle函数。呵,我们现在可以看到 shuffle.c 中的代码了!我们可以控制程序一步一步地执行,并看到究竟发生了什么事。如果你把光标暂停在局部变量上,你将能看到变量的内容。太好了。这比那条 printf 语句好多了,是不是? 小结 本文大体介绍了编译和调试C程序的方法。我们讨论了编译器走过的步骤,以及为了让编译器做这些工作应该给gcc传递哪些选项。我们简述了有关连接共享函数库的问题,最后介绍了调试器。真正了解你所从事的工作还需要付出许多努力,但我希望本文能让你正确地起步。你可以在 gcc、 as 和 ld的 m

20、an 和 info page中找到更多的信息。 自己编写代码可以让你学到更多的东西。作为练习你可以以本文的纸牌游戏为基础,编写一个21点游戏。那时你可以学学如何使用调试器。使用GUI的KDbg开始可以更容易一些。如果你每次只加入一点点功能,那么很快就能完成。切记,一定要保持程序一直能运行! 要想编写一个完整的游戏,你需要下面这些内容: * 一个纸牌玩家的定义(即,你可以把deck_t定义为player_t)。 * 一个给指定玩家发一定数量牌的函数。记住在纸牌中要增加“已发牌”的数量,以便能知道还有那些牌可发。还要记住玩家手中还有多少牌。 * 一些与用户的交互,问问玩家是否还要另一张牌。 * 一

21、个能打印玩家手中的牌的函数。 card 等于value % 13 (得数为0到12),suit 等于 value / 13 (得数为0到3)。 * 一个能确定玩家手中的value的函数。Ace的value为零并且可以等于1或11。King的value为12并且可以等于10。 GCC精彩之旅 zz 为Linux开发应用程序时,绝大多数情况下使用的都是C语言,因此几乎每一位Linux程序员面临的首要问题都是如何灵活运用C编译器。目前 Linux下最常用的C语言编译器是GCC(GNU Compiler Collection),它是GNU项目中符合ANSI C标准的编译系统,能够编译用C、C+和Obj

22、ect C等语言编写的程序。GCC不仅功能非常强大,结构也异常灵活。最值得称道的一点就是它可以通过不同的前端模块来支持各种语言,如Java、 Fortran、Pascal、Modula-3和Ada等。 开放、自由和灵活是Linux的魅力所在,而这一点在GCC上的体现就是程序员通过它能够更好地控制整个编译过程。在使用GCC编译程序时,编译过程可以被细分为四个阶段: 预处理(Pre-Processing) 编译(Compiling) 汇编(Assembling) 链接(Linking) Linux 程序员可以根据自己的需要让GCC在编译的任何阶段结束,以便检查或使用编译器在该阶段的输出信息,或者对

23、最后生成的二进制文件进行控制,以便通过加入不同数量和种类的调试代码来为今后的调试做好准备。和其它常用的编译器一样,GCC也提供了灵活而强大的代码优化功能,利用它可以生成执行效率更高的代码。 GCC提供了30多条警告信息和三个警告级别,使用它们有助于增强程序的稳定性和可移植性。此外,GCC还对标准的C和C+语言进行了大量的扩展,提高程序的执行效率,有助于编译器进行代码优化,能够减轻编程的工作量。 GCC起步 在学习使用GCC之前,下面的这个例子能够帮助用户迅速理解GCC的工作原理,并将其立即运用到实际的项目开发中去。首先用熟悉的编辑器输入清单1所示的代码: 清单1:hello.c #includ

24、e int main(void)printf (Hello world, Linux programming!n);return 0;然后执行下面的命令编译和运行这段程序: # gcc hello.c -o hello# ./helloHello world, Linux programming!从程序员的角度看,只需简单地执行一条GCC命令就可以了,但从编译器的角度来看,却需要完成一系列非常繁杂的工作。首先,GCC需要调用预处理程序 cpp,由它负责展开在源文件中定义的宏,并向其中插入“#include”语句所包含的内容;接着,GCC会调用ccl和as将处理后的源代码编译成目标代码;最后,G

25、CC会调用链接程序ld,把生成的目标代码链接成一个可执行程序。 为了更好地理解GCC的工作过程,可以把上述编译过程分成几个步骤单独进行,并观察每步的运行结果。第一步是进行预编译,使用-E参数可以让GCC在预处理结束后停止编译过程: #gcc -E hello.c -o hello.i此时若查看hello.cpp文件中的内容,会发现stdio.h的内容确实都插到文件里去了,而其它应当被预处理的宏定义也都做了相应的处理。下一步是将hello.i编译为目标代码,这可以通过使用-c参数来完成: #gcc -c hello.i -o hello.oGCC默认将.i文件看成是预处理后的C语言源代码,因此上

26、述命令将自动跳过预处理步骤而开始执行编译过程,也可以使用-x参数让GCC从指定的步骤开始编译。最后一步是将生成的目标文件链接成可执行文件: #gcc hello.o -o hello在采用模块化的设计思想进行软件开发时,通常整个程序是由多个源文件组成的,相应地也就形成了多个编译单元,使用GCC能够很好地管理这些编译单元。假设有一个由foo1.c和foo2.c两个源文件组成的程序,为了对它们进行编译,并最终生成可执行程序foo,可以使用下面这条命令: #gcc foo1.c foo2.c -o foo如果同时处理的文件不止一个,GCC仍然会按照预处理、编译和链接的过程依次进行。如果深究起来,上面

27、这条命令大致相当于依次执行如下三条命令: # gcc -c foo1.c -o foo1.o# gcc -c foo2.c -o foo2.o# gcc foo1.o foo2.o -o foo在编译一个包含许多源文件的工程时,若只用一条GCC命令来完成编译是非常浪费时间的。假设项目中有100个源文件需要编译,并且每个源文件中都包含 10000行代码,如果像上面那样仅用一条GCC命令来完成编译工作,那么GCC需要将每个源文件都重新编译一遍,然后再全部连接起来。很显然,这样浪费的时间相当多,尤其是当用户只是修改了其中某一个文件的时候,完全没有必要将每个文件都重新编译一遍,因为很多已经生成的目标文

28、件是不会改变的。要解决这个问题,关键是要灵活运用GCC,同时还要借助像Make这样的工具。 警告提示功能 GCC包含完整的出错检查和警告提示功能,它们可以帮助Linux程序员写出更加专业和优美的代码。先来读读清单2所示的程序,这段代码写得很糟糕,仔细检查一下不难挑出很多毛病: main函数的返回值被声明为void,但实际上应该是int; 使用了GNU语法扩展,即使用long long来声明64位整数,不符合ANSI/ISO C语言标准; main函数在终止前没有调用return语句。 清单2:illcode.c #include void main(void)long long int var

29、 = 1;printf(It is not standard C code!n);下面来看看GCC是如何帮助程序员来发现这些错误的。当GCC在编译不符合ANSI/ISO C语言标准的源代码时,如果加上了-pedantic选项,那么使用了扩展语法的地方将产生相应的警告信息: # gcc -pedantic illcode.c -o illcodeillcode.c: In function main:illcode.c:9: ISO C89 does not support long longillcode.c:8: return type of main is not int需要注意的是,-pedantic编译选项并不能保证被编译程序与ANSI/ISO C标准的完全兼容,它仅仅只能用来帮助Linux程序员离这个目标越来越近。或者换句话说,-pedantic选项能够帮助程序员发现一些不符合 ANSI/ISO C标准的代码,但不是全部,事实上只有ANSI/ISO C语言标准中要求进行编译器诊断的那些情况,才有可能被GCC发现并提出警告。 除了-p

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1