现在的位置: 首页 > 综合 > 正文

斯坦佛编程教程-Unix编程工具(一)

2013年10月03日 ⁄ 综合 ⁄ 共 3222字 ⁄ 字号 评论关闭

Unix编程工具
作者:Parlante, Zelenski等
Copyright ©1998-2001, 斯坦佛大学
介绍
这篇文章介绍了基于Unix系统编程的整个过程,包括编辑-编译-链接-调试。还极少了几个普遍的Unix编程工具——gcc,make,gdb,emacs还有Unix shell。本文的目标是介绍这些工具的主要特征和典型的使用方式并且足够详细地展示用它们共同来完成简单的项目。我们已经在斯坦佛使用了这篇文章的一个版本来帮助同学来开始Unix编程。

主要内容
介绍——编译-链接的过程
gcc编译器/连接器
make的功能
gdb调试器
emacs编辑器
Unix shell命令概述
http://cslibrary.stanford.edu/

(这是在斯坦佛计算机科学教学库中的#107号文档,叫做Unix编程工具.还有一些其他的免费的教学资料在http://cslibrary.stanford.edu/可以得到。这份文档可以免费使用,和复制或是出售,只要它是完整并且没有被篡改过。)

其它的资源
这个文档只是对工具的一些介绍——想取得某个工具更详细的信息的话,请去查看工具的man页面和xinfo中的词条解释。并且O'Reilly & Associates出版了一系列的关于Unix相关工具的书籍(那些书的封面都会有一种动物)。想学习基础的C语言的话,请查看计算机教育库的#101文档,(http://cslibrary.stanford.edu/101/
)。

编译的过程
在具体的去了解某个工具的细节之前,回顾一下编译一个可执行文件的整个过程将是非常有用的。在源文件被编辑好之后,build的分为两个步骤:编译和链接。每一个源文件(foo.c)都会编译成一个目标文件(foo.o)。每一个目标文件都包含着一个系统的依赖,编译成程序的一个代表,就如同它在源文件中描述的那样。一个目标文件的文件名和源文件的文件名事一样的,但是扩展名变成了“.o”——“main.c”编译成了“main.o”.这个.o文件将会包含程序所需要的引用,符号,方法,变量等等。
每一个单独的目标文件接下来就可以链接在一起并生成一个可以在系统上运行的可执行文件。链接这一步也会将一些包含了库方法(例如printf()和malloc())的库文件链接进来。整个的过程如下图:

第一部分——gcc
接下来我们要讨论的事gcc编译器,一个GNU(www.gnu.org)项目的开源产品。使用gcc有下面几个好处——它会一直更新,并且十分可靠,它可以在多个平台运行,当然,它是免费和开源的。Gcc能编译C,C++和objective-C。Gcc实际上既是一个编译器又是一个链接器。对于简单的问题,一个简单的调用就能完成整个的编译和链接操作。例如,对于一个简单的项目你可能会使用下面的命令,作用是将三个.c文件编译链接在一起并生成一个叫做“program”的可执行文件。

gcc main.c module1.c module2.c -o program

上面的一行命名可以等价地分开写成三条命令来建立项目。

gcc -c main.c ## Each of these compiles a .c
gcc -c module1.c
gcc -c module2.c
gcc main.o module1.o module2.o -o program ## This line links the .o's to build the program

调用gcc的一般命令是:
gcc options files

option一系列用于控制编译器的选项,而files是gcc用于读取或是写入的列表

命令的选项

像大多数的Unix程序一样,gcc支持大量的命令选项来控制它的运行。它们在gcc的man页面都有介绍。我们能够很安全地忽略掉其中大部分的命令选项,但下面几个经常使用到的我们得注意:-c,-o,-g,-Wall,-I,-L和-l。

-c files 
直接用gcc将源文件编译到目标文件而忽略掉链接的阶段。Makefiles(下面会介绍)用这个选项来将多个文件一次性编译。

-o file
设置gcc的输出文件的文件名必须是file。如果这个选项没有被指明,那么默认的名字得看情况。如果事编译一个.c的源文件,输出的目标文件将会和源文件同名,但扩展名是.o。如果事链接生成一个可执行文件,输出文件将会被命名成a.out。大部分的时候,-o选项都只是用于在链接生成可执行文件时设置输出文件的文件名,而在编译的时候,人们只是用默认的设置来代替。

-g
指示编译器在输出中包含额外的调试信息。我们建议你在编译你的源文件时加上这个选项,因为我们鼓励你通过使用一些像gdb(下面会介绍)的调试器来变得更加熟练。

-Wall
给出源代码里可能存在的错误。使用了-Wall所得到的提示并不是真正的错误,他们只是编译器认为可能的错误。我们非常建议你在编译你的代码的时候加上-Wall命令。在编译的时候发现错误比在运行事发现错误简单一万倍。-Wall命令就像一个唠叨的大妈,但她值得陪伴在你身边。如果一个学生拿着一个运行不了的代码来请教我,并且这些代码在用-Wall命令编译的时候产生了一些警告,那么30%这些警告就是问题线索,30%可能不是,但你应该很开心它给了你免费的bug提示。

一些时候-Wall命令所得到的警告并不是真正的问题,代码是正确的,并且编译器也通过了编译。这个时候,请不要忽视那些警告,请解决源代码所产生的所有警告。放任警告不管是一个坏习惯。

下面事一个小的代码片段在一句代码中给变量赋值并判断其是否为真。
int flag;
if (flag = IsPrime(13)) {
...
}

编译器会给出一个“可能不经意赋值”的警告,尽管这个情况中赋值事正确的,这个警告将会捕捉到一个普遍性的bug,就是当你想要输入==的时候却输入了=。为了解决这个警告,重写这个测试代码...
int flag;
if ((flag = IsPrime(13)) != 0) {
...
}

这样写就不会有警告了,而生成的代码和之前的是一样的。或者,你可以将整个测试代码放入一个大括号里表明你的用意。这就是使用-Wall帮你找bug的价值。

-Idir
将dir目录加入#include命令的搜索命令。编译器会自动地搜索几个标准目录。使用这个选项可以增加编译器的搜索目录。“-I”命令和目录名之间没有空格。如果因为#include命令无法找到文件而编译失败,你需要用-I来解决它。

下面事怎样利用unix中的“find”命令来查找你的#include文件。这个例子查找/user/include目录下的所有含有“inet”字段的头文件...
nick% find /usr/include -name '*inet*'
/usr/include/arpa/inet.h
/usr/include/netinet
/usr/include/netinet6

-lmylib
(l是小写)在链接的时候为未实现的标志(方法或是全局变量)搜索名为mylib的库。库的真正的名字应该事libmylib.a,并且这个库要么可以在系统的默认路径下可以找到,要么可以在用-L命令(下面会提到)添加的目录中找到。

链接时,-l标志在命令列表中的位置事非常重要的,因为链接器不会在先前检查过的库中搜索未定义的标志。例如,如果你在调用已一个依赖math库的库时,它必须在链接math库之前被链接,否这就会报链接错误。和上面的那个命令一样,命令符和库名之间没有空格,并且,这是一个小写的“L”,而不是数字1,如果在链接阶段因为一个标志未找到而失败了,你就需要一个-l命令来加上合适的库了,或者你可编译的时候写错了函数的名字或是变量的名字。

-Ldir
将dir加入到由-l命令指明的库的搜索目录。和上面的那个命令一样,命令符和目录名之间没有空格。如果在链接的时候因为一个库文件没有找到,你需要用-L命令来解决一下,或者你库名写错了。

【上篇】
【下篇】

抱歉!评论已关闭.