




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、要想读懂本文,你需要对 C语言有基本的了解,本文将介绍如何使用gcc编译器。首先,我们介绍如何在命令行方式下使用编译器编译简单的C源代码。然后,我们简要介绍一下编译器究竟作了那些工作,以及如何控制编译过 程。我们也简要介绍了调试器的使用方法。GCC rules你能想象使用封闭源代码的私有编译器编译自由软件吗?你怎么知道编译 器在你的可执行文件中加入了什么?可能会加入各种后门和木马。Ken Thompson是一个著名的黑客,他编写了一个编译器,当编译器编译自己 时,就在'login'程序中留下后门和永久的木马。请到这里阅读他对这个杰作的描 述。幸运的是,我们有了 gcco 当你进
2、行 con figure;make;make in stall时,gcc在 幕后做了很多繁重的工作。如何才能让gcc为我们工作呢?我们将开始编写一个纸牌游戏,不过我们只是为了演示编译器的功能,所以尽可能地精简了代 码。我们将从头开始一步地做,以便理解编译过程,了解为了制作可执行文件 需要做些什么,按什么顺序做。我们将看看如何编译C程序,以及如何使用编译选项让gcc按照我们的要求工作。步骤(以及所用工具)如下:预编译(gcc -E)编译(gcc),汇编(as),和连接(Id)。开始 .首先,我们应该知道如何调用编译器。实际上,这很简单。我们将从那个 著名的第一个C程序开始。(各位老前辈,请原谅我
3、)。#include <stdio.h>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那样的命名限制。gc
4、c -o game game.cgameHello World到现在为止,我们离一个有用的程序还差得很远。如果你觉得沮丧,你可 以想一想我们已经编译并运行了一个程序。因为我们将一点为这个程序添加功 能,所以我们必须保证让它能够运行。似乎每个刚开始学编程的程序员都想一 下子编一个 1000 行的程序,然后一次修改所有的错误。没有人,我是说没有 人,能做到这个。你应该先编一个可以运行的小程序,修改它,然后再次让它 运行。这可以限制你一次修改的错误数量。另外,你知道刚才做了哪些修改使 程序无法运行,因此你知道应该把注意力放在哪里。这可以防止这样的情况出 现:你认为你编写的东西应该能够工作,它也能通过
5、编译,但它就是不能运 行。请切记,能够通过编译的程序并不意味着它是正确的。下一步为我们的游戏编写一个头文件。头文件把数据类型和函数声明集中 到了一处。这可以保证数据结构定义的一致性,以便程序的每一部分都能以同 样的方式看待一切事情。#ifndef DECK_H#define DECK_H#define DECKSIZE 52typedef struct deck_tint cardDECKSIZE;/* number of cards used */int dealt;deck_t;#endif /* DECK_H */把这个文件保存为deck.h。只能编译.c文件,所以我们必须修改game.
6、c。 在game.c的第2行,写上#include"deck.h"。在第5行写上deck_tdeck;。为了保 证我们没有搞错,把它重新编译一次。gcc -o game game.c如果没有错误,就没有问题。如果编译不能通过,那么就修改它直到能通 过为止。预编译编译器是怎么知道deck_t类型是什么的呢?因为在预编译期间,它实际上 把"deck.h"文件复制到了 "game.c"文件中。源代码中的预编译指示以"#"为前缀。 你可以通过在gcc后加上-E选项来调用预编译器。几乎有 3200 行的输出!其中大多数来自
7、stdio.h 包含文件,但是如果你查 看这个文件的话,我们的声明也在那里。如果你不用-o选项指定输出文件名的话,它就输出到控制台。预编译过程通过完成三个主要任务给了代码很大的灵 活性。把"in clude"的文件拷贝到要编译的源文件中。用实际值替代"define"的文本。在调用XX的地方进行XX替换。这就使你能够在整个源文件中使用符号常量(即用DECKSIZ表示一付牌中的纸牌数量),而符号常量是在一个地方定义的,如果它的值发生了变化,所 有使用符号常量的地方都能自动更新。在实践中,你几乎不需要单独使用-E选项,而是让它把输出传送给编译器。编译作为一个中
8、间步骤, gcc 把你的代码翻译成汇编语言。它一定要这样做,它 必须通过分析你的代码搞清楚你究竟想要做什么。如果你犯了语法错误,它就 会告诉你,这样编译就失败了。人们有时会把这一步误解为整个过程。但是,实际上还有许多工作要 gcc 去做呢。汇编as把汇编语言代码转换为目标代码。事实上目标代码并不能在 CPU上运 行,但它离完成已经很近了。编译器选项-c把.C文件转换为以.0为扩展名的目 标文件。如果我们运行 gcc -c game.c我们就自动创建了一个名为game.o的文件。这里我们碰到了一个重要的问 题。我们可以用任意一个.c文件创建一个目标文件。正如我们在下面所看到 的,在连接步骤中我们
9、可以把这些目标文件组合成可执行文件。让我们继续介 绍我们的例子。因为我们正在编写一个纸牌游戏,我们已经把一付牌定义为 deck_t,我们将编写一个洗牌函数。这个函数接受一个指向deck类型的指针,并把一付随机的牌装入deck类型。它使用'drawn'数组跟踪记录那些牌已经用过 了。这个具有DECKSIZ个元素的数组可以防止我们重复使用一张牌。#include <stdlib.h>#include <stdio.h>#include <time.h>#include "deck.h"static time_t seed =
10、0;void shuffle(deck_t *pdeck)/* Keeps track 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 < DECKSIZE; i+)int value = -1;dovalue = rand() % DECKSIZE;while(drawnvalue != 0);/* mark value as u
11、sed */drawnvalue = 1;/* debug statement */printf("%in", value);pdeck->cardi = value;pdeck->dealt = 0;return;把这个文件保存为shuffle.c。我们在这个代码中加入了一条调试语 句,以便运行时,能输出所产生的牌号。这并没有为我们的程序添加功能,但 是现在到了关键时刻,我们看看究竟发生了什么。因为我们的游戏还在初级阶 段,我们没有别的办法确定我们的函数是否实现了我们要求的功能。使用那条 printf 语句,我们就能准确地知道现在究竟发生了什么,以便在开始下一
12、阶段之 前我们知道牌已经洗好了。在我们对它的工作感到满意之后,我们可以把那一 行语句从代码中删掉。这种调试程序的技术看起来很粗糙,但它使用最少的语 句完成了调试任务。以后我们再介绍更复杂的调试器。请注意两个问题。我们用传址方式传递参数,你可以从 '&' (取地址)操作符看出来。这把变 量的机器地址传递给了函数,因此函数自己就能改变变量的值。也可以使用全 局变量编写程序,但是应该尽量少使用全局变量。指针是C的一个重要组成部分,你应该充分地理解它。我们在一个新的.c文件中使用函数调用。操作系统总是寻找名为'main'的函 数,并从那里开始执行。shuffle
13、.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:In fun cti on 'm
14、a in':/tmp/ccmiHnJX.o(.text+0xf):undefined reference to 'shuffle'collect2: ld returned 1 exit status编译成功了,因为我们的语法是正确的。但是连接步骤却失败了,因为我 们没有告诉编译器'shuffle'函数在哪里。那么,到底什么是连接?我们怎样告诉 编译器到哪里寻找这个函数呢?连接连接器Id,使用下面的命令,接受前面由as创建的目标文件并把它转换为 可执行文件 gcc -o game game.o shuffle.o这将把两个目标文件组合起来并创建可执行文件
15、 game。连接器从 shuffle.o 目标文件中找到 shuffle 函数,并把它包括进可执行文 件。目标文件的真正好处在于,如果我们想再次使用那个函数,我们所要做的 就是包含"deck.h"文件并把shuffle.o目标文件连接到新的可执行文件中。象这样的代码重用是经常发生的。虽然我们并没有编写前面作为调试语句 调用的printf函数,连接器却能从我们用#include<stdlib.h>语句包含的文件中找 到它的声明,并把存储在 C库(/lib/libc.so.6 )中的目标代码连接进来。这种方 式使我们可以使用已能正确工作的其他人的函数,只关心我们所要
16、解决的问 题。这就是为什么头文件中一般只含有数据和函数声明,而没有函数体。一 般,你可以为连接器创建目标文件或函数库,以便连接进可执行文件。我们的 代码可能产生问题,因为在头文件中我们没有放入任何函数声明。为了确保一 切顺利,我们还能做什么呢?另外两个重要选项-Wall 选项可以打开所有类型的语法警告,以便帮助我们确定代码是正确 的,并且尽可能实现可移植性。当我们使用这个选项编译我们的代码时,我们 将看到下述警告:game.c:9: warning:implicit declarati on of fun cti on 'shuffle'这让我们知道还有一些工作要做。我们需要在
17、头文件中加入一行代码,以 便告诉编译器有关 shuffle 函数的一切,让它可以做必要的检查。听起来象是一 种狡辩,但这样做可以把函数的定义与实现分离开来,使我们能在任何地方使 用我们的函数,只要包含新的头文件并把它连接到我们的目标文件中就可以 了。下面我们就把这一行加入 deck.h 中。void shuffle(deck_t *pdeck);这就可以消除那个警告信息了。另一个常用编译器选项是优化选项-0#(即-02)。这是告诉编译器你需要什么 级别的优化。编译器具有一整套技巧可以使你的代码运行得更快一点。对于象我们这种 小程序,你可能注意不到差别,但对于大型程序来说,它可以大幅度提高运行
18、速度。你会经常碰到它,所以你应该知道它的意思。调试我们都知道,代码通过了编译并不意味着它按我们得要求工作了。你可以使用下面的命令验证是否所有的号码都被使用了game | sort - n | less并且检查有没有遗漏。如果有问题我们该怎么办?我们如何才能深入底层 查找错误呢?你可以使用调试器检查你的代码。大多数发行版都提供著名的调 试器:gdb。如果那些众多的命令行选项让你感到无所适从,那么你可以使用KDE提供的一个很好的前端工具 KDbgo还有一些其它的前端工具,它们都很相似。 要开始调试,你可以选择File->Executable然后找到你的game程序。当你按下 F5键或选择Ex
19、ecution->从菜单运行时,你可以在另一个窗口中看到输出。怎么 回事?在那个窗口中我们什么也看不到。不要担心,KDbg没有出问题。问题在于我们在可执行文件中没有加入任何调试信息,所以KDbg不能告诉我们内部发生了什么。编译器选项 -g 可以把必要的调试信息加入目标文件。你必须用这个选项编 译目标文件(扩展名为.0),所以命令行成了:gcc -g -c shuffle.c game.cgcc -g -o game game.o shuffle.o这就把钩子放入了可执行文件,使 gdb和KDbg能指出运行情况。调试是一 种很重要的技术,很值得你花时间学习如何使用。调试器帮助程序员的方法是 它能在源代码中设置 断点”现在你可以用右键单击调用 shuffle函数的那行代 码,试着设置断点。那一行边上会出现一个红色的小圆圈。现在当你按下 F5键 时,程序就会在那一行停止执行。按 F8可以跳入shuffle函数。呵,我们现在可 以看到shuffle.c中的代码了!我
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 广西贺州市本年度(2025)小学一年级数学统编版期中考试((上下)学期)试卷及答案
- 江西省抚州市临川区一中2025年高考英语考前最后一卷预测卷含答案
- 2025年天津市七校静海一中杨村中学高三压轴卷英语试卷含答案
- 临床诊断学模拟练习题(附答案)
- 山西省晋城市部分学校2024-2025学年高一下学期期中考试数学试题(原卷版+解析版)
- 电缆线路敷设工程中的质量控制与验收流程考核试卷
- 粮食储备的社会责任考核试卷
- 再生物资回收在绿色供应链中的作用考核试卷
- 稀土金属提炼过程中的政策法规研究与实践考核试卷
- 职场情绪智能与人际沟通考核试卷
- 职业生涯规划与个人职业发展培训课件
- NB-T 47015-2011(JB-T 4709) 压力容器焊接规程
- 五大策略成就燕赵零售王-记石家庄北国超市
- 建立世界贸易组织协定(中英)
- 农民工法律维权知识讲座
- 《流感嗜血杆菌》课件
- 《禾字旁》名师课件
- 肺胀病(慢性阻塞性肺疾病)中医临床路径
- 中央分隔带填土规范
- 深基坑专项施工方案专家论证会议签到表
- 强化学习与联邦学习结合
评论
0/150
提交评论