为什么Pascal不是我最喜欢的程序设计语言

 

为什么Pascal不是我最喜欢的程序设计语言
Why Pascal is Not My Favorite Programming Language

Brian W. Kernighan, April 2, 1981
AT&T Bell Laboratories,  Murray Hill, New Jersey 07974

翻译:ljhhh0123@yahoo.com.cn
为了学习计算机英语,我根据google的自动翻译,再加润色,本文并未完成,但我会随时更新,如有更好的翻译,请发给我。谢谢。

摘要
Pascal程序设计语言已成为计算机科学的教育教学的主要语言。它还强有力的影响了其后语言的开发,特别是后来的Ada语言。

Pascal最初的主要目的是作为教学语言,但它已被越来越多的被建议用来进行严肃的程序设计。而且,如对于系统程序设计任务,甚至是操作系统的编写。

Pascal,至少在它的标准形式,简单的说来,不适合进行严肃的程序设计。本文讨论了我个人的一些原因。

1. 起源

本文起源于两个事件:有大量的论文来比较C语言和Pascal(1,2,3,4)语言,和我个人决定用Pascal重写《软件工具》(5)一书。

比较C和Pascal是有点像比较Learjet(喷气式飞机)和Piper Cub(微型活塞式飞机),一个是获取工作,而另一种是学习如何工作。所以这种比较往往是有点牵强。但是,《软件工具》一书的修正版本会有更多相关的比较。那里的程序起始是用Ratfor语言编写,一个“结构化的”Fortran方言的预处理器实现。由于Ratfor是伪装的Fortran,它有一些Pascal的东西:数据类型更适合于字符处理,数据结构能力更强,和强类型用来强制数据的真实可靠。

结果是,用Pascal重写程序,比我预期的更难。本文就是试图提取有关Pascal的编程适宜性(不同于编程学习)的一些的经验教训。这不是拿C或Ratfor与Pascal进行的比较。

程序最早的应用是用pascal方言用来解释圆周率,由加州大学伯克得分校提供。语言接近Jensen和Wirth名义上的标准(6),具有良好的诊断和细心的运行时检查。此后,程序已经可以运行,除了新的基本函数库不用改变,还在其他四个系统:一阿姆斯特丹自由大学(以下简称VU为自由大学)的解释器,一个伯克利系统的VAX版本(一个真正的编译器),一个Whitesmiths责任公司供应编译器,和加州大学圣地亚哥分校在Z80上的Pascal。但所有这些Pascal系统最后都用C编写。

Pascal是讨论得最多的语言。最近书目(7)列出了175项讨论、分析和辩论的标题。最经常引用的论文(值得一读)是Habermann的强烈批判(8)和Lecarme和Desjardins同样强烈的反驳(9)。Boom和DeJong的文章(10)也是良好的读物。Wirth自己对Pascal的评价在[11]。我没有想要或能力来概括这些文字;本文代表我个人的意见和大部分重复别人提出的意见。我试着组织围绕问题的其他材料

    ·类型和作用域
    ·控制流
    ·环境
    ·修饰
   
并在每个方面或多或少减少顺序的重要性。

我先表示我的结论:Pascal可能是一种令人钦佩教初学者如何编程的语言;我没有第一手经验。这是1968年的一个相当大的成就。这无疑影响了最近的语言的设计,其中Ada很可能是最重要的设计。但在它的标准形式(包括现有的和计划的),Pascal不能足够的胜任编写真正的程序。它仅适用于小型的,独立的程序,并且与环境交互能力差。这使人写出的软件都没有多大用。

2. 类型和作用域
Pascal(差不多)是强类型语言。粗略地说,这意味着每一个程序对象具有良好定义的类型,它隐式的定义了操作对象的合法值。语言的保证,它会禁止一些非法值和操作,通过编译和运行时检查。当然,编译器可能不会完成所有的语言定义的隐含检查。此外,强类型不会与量纲分析相混淆。如果一个定义类型的'apple'和'orange'

    type
        apple = integer;
        orange = integer;

然后涉及apple和oranger的任意数学表达式,都是完全合法的。

强类型有不同的方式。例如,函数和过程的参数的类型匹配检查。在过去,Fortran自由的传递浮点参数到子程序会被认为是一个整数。我认为Pascal的这个特性是令人满意的,因为这样构造的话肯定会导致错误警告。

整型变量可以被声明为有一个范围的合法值,编译器和运行时支持确保一个大的整型变量不能放进一个小的整型变量,这也似乎是一种服务,虽然运行时检查是一种强制的处罚。

让我们继续前进的类型和作用域的一些问题。

2.1  数组的大小是其类型的一部分

如果一个声明

     var     arr10 : array [1..10] of integer;
             arr20 : array [1..20] of integer;


然后arr10和arr20分别是10和20个整数的数组。假设我们要编写一个过程'sort'来排序一个整数数组。由于arr10和arr20有不同的类型,它是不可能编写一个简单的过程来将它们排序。

这些地方会特别影响软件工具,我想一般的程序是,可以创建一个程序库,它可以真正的做一些一般的事和通用流行的操作,比如排序。

在特定的数据类型最常见的影响是 'array of char',在Pascal的字符串是一个字符数组。考虑编写一个函数'index(s,c)',将返回字符c在字符串s第一次出现的位置,如果它不存在就返回0。问题是如何处理'index'的字符串参数。调用"index('hello',c)"和调用"index('goodbye',c)"不能同时是合法的,因为字符串有不同的长度。 (我忽略的是如何检测像'hello'常量字符串结尾的问题,因为它不能。)接下来的尝试是

    var     temp : array [1..10] of char;
            temp := 'hello';

            n := index(temp,c);


但对赋值给'temp'非法的,因为'hello'和'temp'的长度不同。

从这个无限的倒退中逃脱的唯一方法就是要为每个可能的字符串的大小定义一个成员函数,或者使所有字符串具有相同长度的(包括常量字符串'define')。

后一种方法是两大害中较轻的。在'Tools'中,'string'类型被声明为

     type    string = array [1..MAXSTR] of char;


其中常数“MAXSTR”是"足够大",所有程序的所有字符串,都恰好正是这种大小。这是很不理想,虽然它使人们有可能获得程序的运行。它不解决创造真正有用的例程库的问题。

在一些情况下,根本不能接受的使用固定大小的数组表示。例如,使用'Tools'程排序文本行就要用一些文本行来填充内存,它的运行时间在很大程度上取决于如何充分的填充内存。

因此,对于'sort',另一种是用来表示一个字符数组和一个到这个数组索引:

     type    charbuf = array [1..MAXBUF] of char;
             charindex = array [1..MAXINDEX] of 0..MAXBUF;


但是,过程和函数处理固定长度的形式不能使用可变长度的形式,一个完整的程序集需要复制和比较这种表示的字符串。这在Fortran或C中,一个函数就够了。

如上所述,一个常量字符串写成

     'this is a string'

并有型'packed array [1..n] of char',其中n是长度。因此,不同长度的字符串具有不同的类型。唯一的方法是,把输出的消息都处理成一样的最大长度。

     error('short message                    ');
     error('this is a somewhat longer message');


许多商业Pascal编译器提供一个'string'的数据类型,来明确避免这个问题;'string'被视为同一类型而无论大小。这解决了这一单个数据类型的问题,仅此而已。但它也未能解决第二个问题,比如计算一个常量字符串的长度,另外,内置的功能是通常的解决方案。

Pascal爱好者经常声称,为应对数组大小的一个问题不过是复制一些库程序和填写参数的程序在手。但这种抵御的声音大都是微弱的:(12)

``由于数组的边界是类型(或更确切地说是它的索引类型)的一部分,它不可能定义一个过程或函数应用于数组的不同边界上。虽然这种限制可能会成为一个严重的问题,我们使用Pascal的经验表明,它发生的非常频繁。[...]然而,需要绑定的参数数组的大小是使用有关的程序库严重缺陷。''
这种笨拙是Pascal最大的一个问题。我相信,如果它能被修复,语言的应用将会扩大一个数量级。建议的ISO标准为Pascal(13)提供这样一种修复(``符合性数组模式''),但接受这个作为标准的一部分显然仍存在疑问。

2.2  没有静态变量和初始化

一个'static'变量(在使用Algol的地方通常被称为一个'own'变量)对一些程序是私有的,并保留其值从一个程序调用到下一个调用。事实上,Fortran的变量是内部静态的,除了COMMON;在C语言里“static”声明,可以应用到局部变量。 (严格地说在Fortran 77,必须使用SAVE强制静态属性。)

Pascal没有这样的存储类。这意味着,如果一个Pascal函数或过程打算从一个调用到另一个调用之间记住一个值时,必须使用具有外部变量的函数或过程。因此,对于其他程序它必须是可见的,其名称必须在更大的范围内是唯一的。这种问题的一个简单的例子是一个随机数发生器:用于计算输出值必须保存到计算下一个,所以它必须是一个变量,其生存期包含了随机数生成所有调用存储。实际上,这通常是最外层的程序块。因此,这样的变量声明是远离它实际使用的地方。

一个例子来自于《工具(Tools)》一书第7章所描述的文本格式化。变量 'dir' 控制从行调整插入多余的空白行,获得左,右交替插入的方向。在Pascal中,代码如下:

     program formatter (...);

     var
             dir : 0..1;     { direction to add extra spaces }
             .
             .
             .
     procedure justify (...);
     begin
             dir := 1 - dir; { opposite direction from last time }
             ...
     end;
             ...

     begin { main routine of formatter }
             dir := 0;
             ...
     end;

变量'dir'的声明、初始化和使用分散在各处,相距数百行。在C或Fortran里,'dir'可在程序需要时做成私用的:
              ...
     main()
     {
             ...
     }
             ...

     justify()
     {
             static int dir = 0;

             dir = 1 - dir;
             ...
     }

当然,还有一个更大的规模上相同的问题很多其他的例子,对函数缓冲I/O,存储管理和符号表中的所有映入脑海。
至少有两个相关的问题。Pascal没有提供方法来初始化静态变量(即在编译时),也没有什么类似于Fortran的DATA语句或类似的初始化:

     int dir = 0;

在C语言里,这意味着,一个Pascal程序必须包含明确的赋值语句来初始化变量,如:

     dir := 0;

这使得程序的源代码文本更大,而且在运行时体积也更大。

此外,缺乏初始化加剧了大范围的问题,这都归因于缺乏静态存储类。初始化的时机是在开始的时候,所以无论是主程序本身带有大量的初始化代码,或者调用一个或多个程序做初始化。在这两种情况下,要初始化的变量必须是可见的,这实际上意味着在层次结构的最高水平。其结果是,任何变量,必须在全局范围进行初始化。

第三个困难是,没有两个例程来共享一个变量的方式,除非它是至少达到或超过他们的共同祖先声明。 FORTRAN COMMON和C的外部静态存储类都提供了两个例程私下合作的方式,没有分享他们的祖先的信息。

新标准不提供静态变量,初始化或无层级的通讯。

2.3 相关程序的组件必须分开

由于原始的Pascal是用一单遍编译器实现的,语言坚信在使用前先声明。特别是,过程和函数必须先声明(函数体和所有),然后再使用它们。其结果是,一个典型的Pascal程序从下往上读 - 所有的程序和功能显示在代码的各级调用之前。其中的功能设计和使用的顺序基本上是相反的。
C和Ratfor的设施#include在一定程度上可以缓解:源文件可以在需要时导入进来,而不使程序杂乱。 #include不是标准Pascal的一部分,虽然UCB,VU和Whitesmiths编译器都提供了。

还有一种'forward'的声明,允许在Pascal分离的功能还是从机构的议事头声明,它是定义相互递归程序之意。当身体被声明以后,关于该声明头可能只包含函数的名称,不得重复从第一个实例的信息。

一个相关的问题是,Pascal有着严格的顺序,它愿意接受声明。每个过程或函数包括

label label declarations, if any
const constant declarations, if any
type type declarations, if any
var variable declarations, if any

procedure and function declarations, if any
begin
body of function or procedure
end


这意味着为了编译器的方便,类型的所有声明必须组合在一起的,即使程序员想保持在一起的逻辑相关,以便了解程序更好的东西。由于方案将提交给编译器一次全部,它几乎是不可能保持的声明,初始化和变量的类型和并拢的使用。即使是最敬业的Pascal的一些支持者同意:(14)
``由于无法使这些组件构建大型程序,这是Pascal的最令人沮丧的限制之一。'
一个文件包含工具帮助这里只有一点点。
新标准不放松对声明的顺序的要求。

2.4  不能分开编译(There is no separate compilation)

官方的Pascal语言不提供单独的编译,要做什么取决于实现。有些(例如伯克利的实现)完全禁止它,这是最接近语言的精神和字面意义上完全匹配的。许多人提供了一个声明,表示指定一个函数体外部定义。在任何情况下,所有这些机制不规范,不同的系统又不同。
从理论上讲,没有单独编译的需要 - 如果一个编译器是非常快的(如果所有程序源总是可用的,如果一个人的编译器有一个文件包含工具,而不需要源的多个副本),则重新编译一切是等价的。在实践中,当然,编译器永远不会足够快,而源通常是隐藏的,文件包含是不是语言的一部分,所以这种变化是费时的。

有些系统允许单独编制,但不验证跨边界类型的一致性。这将创建一个强类型的巨洞。 (大多数其他语言做无交叉编译检查要么??,所以Pascal没有这方面的劣势。)我已经看到至少有一个文件(大发慈悲未公布)上页严惩未能在单独的编译边界检查类型C同时建议第N +1的办法,以应付Pascal是编译程序分开,以避免类型检查。

新标准不提供单独编译。

2.5  类型和作用域的一些杂项问题(Some miscellaneous problems of type and scope)

以下几点大多是轻微的刺激,但我在某个地方坚持这几点。
这是不合法的名称,过程的文字名形参不是基本类型,下面是不允许的:

     procedure add10 (var a : array [1..10] of integer);


相反,我们必须发明一种类型的名称,做一个类型声明,并宣布正式参数是一个该类型的实例:

     type    a10 = array [1..10] of integer;
     ...
     procedure add10 (var a : a10);


当然,如果是类型声明是从过程上物理分离的。该发明的类型名称是对于那些经常使用的类型是有帮助的,但它是一个只使用一次的麻烦。
我很高兴可以声明“var”作为函数或过程的形参;过程中明确指出,它打算修改参数。但调用程序没有办法声明一个修改的变量 - 信息只在一个地方,而两地会更好。 (半个面包总比没有好,虽然 - Fortran告诉用户不需要变量。)

这也是一个小麻烦,数组是按值传递的默认-净效果是,每个数组参数声明“VAR”所没有的思维程序员或多或少。如果“VAR”申报不小心忽略,由此产生的错误是潜移默化的。

Pascal的“set”构造似乎是一个好主意,概念上的便利,并提供一些免费的类型检查。例如,一组测试,如

    if (c = blank) or (c = tab) or (c = newline) then ...

可写,而更清楚,也许更有效地为

     if c in [blank, tab, newline] then ...

但在实践中,集合类型是没有用的远不止这些,因为一个集大小强烈依赖于实现(可能是因为它是在原来的疾病预防控制中心的执行情况,59位)。例如,它很自然地尝试写功能“isalphanum(三)”(``是c字母?'')作为

     { isalphanum(c) -- true if c is letter or digit }
     function isalphanum (c : char) : boolean;
     begin
             isalphanum := c in ['a'..'z', 'A'..'Z', '0'..'9']
     end;

但在许多实现Pascal(包括原)这段代码会失败,因为集只是太小了。因此,一般最好设置闲置如果打算编写可移植程序。 (这个具体的例行还运行了一个数量级的集速度比射程试验或数组的引用。)

2.6  没有逃脱

在必要时没有覆盖类型的机制,没有类似C语言的"cast(转型)"机制,这意味着它是不可能用Pascal写出这样的存储分配器或I/O系统的方法,因为没有这样谈论的对象的类型,他们的回报,也没有办法强迫到另一个使用任意类型的对象等。 (严格地说,是有近似变量记录的类型检查是个大漏洞,使得一些其他形式的非法类型可以匹配。)

3. 控制流

Pascal控制流的缺陷是次要的,但很多 - 无数个死亡点,而不是单一的朝向至关重要的地方。
不存在的逻辑运算符“and”与“or”来保证求值的顺序 -不像在C里的&&和无||,它会与大多数其他语言共享,伤害通常在于闭环控制:

     while (i <= XMAX) and (x[i] > 0) do ...

使用Pascal是极不明智的,因为没有办法保证,i是在X[i]之前测试的。

顺便说一下,在这个代码中的括号是强制性的语言已与底部有关联的只有四个运算符优先级的水平。

退出循环没有“break”为语句。这与结构化编程的支持者所信奉的一个入口,一个出口的理念一致,但它确实导致累赘或重复的代码,特别是在与无法控制的,其中的逻辑表达式的计算顺序耦合。考虑这种常见的情况,在C或Ratfor表示:

     while (getnext(...)) {
             if (something)
                     break
             rest of loop
     }

由于没有“break”语句,使用Pascal的第一次尝试是

     done := false;
     while (not done) and (getnext(...)) do
             if something then
                     done := true
             else begin
                     rest of loop
             end

但是这样并不能工作,因为没有办法迫使“not done”是前“getnext”下次调用评估。这导致,经过多次错误的开始,到

     done := false;
     while not done do begin
             done := getnext(...);
             if something then
                     done := true
             else if not done then begin
                     rest of loop
             end
     end

当然惯犯可使用“goto”和一个标签(数字仅它必须声明)退出循环。否则,提前退出是一个痛苦,几乎总是需要一个布尔变量的发明和一定量的狡猾。比较发现在一个Ratfor数组的最后一个非空白:

     for (i = max; i > 0; i = i - 1)
             if (arr(i) != ' ')
                     break

使用Pascal:

     done := false;
     i := max;
     while (i > 0) and (not done) do
             if arr[i] = ' ' then
                     i := i - 1
             else
                     done := true;

一个'for'循环指数为外循环定义,所以它不可能找出一个人是否到了最终还是没有。一个'for'循环增量只能是+1或-1,未成年人的限制。
有没有“回报”的说法一出原因再于一身。函数返回值通过设置一个伪变量(如FORTRAN)值,然后脱落函数结束。这有时会导致扭曲,以确保所有路径实际得到的与正确的价值函数结束。也没有标准的方法来终止除非到达最外层块结束的执行,虽然许多实现提供了一个“停止”,导致立即终止。

在“案例”的说法是更好的设计比C,除了没有“default”条款和行为是不确定的,如果输入的表达式不匹配任何个案。这一重要遗漏呈现“案例”构造几乎一文不值。在超过6000行Pascal在“Software Tools in Pascal(Pascal软件工具)”,我用它只有四次,但如果出现了一个“default”,一个“case”将服务至少在十几个地方。

新标准的机制对这些问题没有任何的帮助。

4. 环境

在Pascal运行时环境相对稀少,而且没有扩展机制,也许除了“官方”语言的源码库机制。

Pascal的内置I/O有一个当之无愧的坏名声。它坚信面向记录的输入和输出。它也有一个超前约定交互环境中是很难实现的。基本上,问题是,I/O系统认为,它必须读取正在处理的记录一个记录之前。在一个互动系统,这意味着,当一个程序启动时,它的第一个操作就是尽量阅读前,本身已执行程序的任何为输入的第一线终端。但在计划

     write('Please enter your name: ');
     read(name);
     ...

预读导致程序挂起,等待输入打印前的提示,它要求。
它可以逃脱这个I/O设计的恶果最受非常认真执行,但不是所有的Pascal系统,这样做,而且在任何情况下,它是相对昂贵。

在I/O设计体现了原有的操作系??统赖以Pascal的设计,甚至沃斯承认偏见,虽然不是它的缺陷(15)假设该文本文件的记录组成,也就是说,文本行。当一行的最后一个字符被读取后,内置的功能“eoln”变为真;在这一点上,我们必须称之为“readln来启动一个新的阅读线和复位??”eoln“。同样,当最后一个字符的文件被读取时,内置的“EOF”变为真。在这两种情况下,“eoln”和“EOF”必须进行测试之前每个“读”,而不是之后。

鉴于此,相当大的痛苦,必须采取合理的模拟输入。这样的“GETC”工程大学伯克利分校和VU I/O系统,但实施可能并不一定为别的:

     { getc -- read character from standard input }
     function getc (var c : character) : character;
     var
             ch : char;
     begin
             if eof then
                     c := ENDFILE
             else if eoln then begin
                     readln;
                     c := NEWLINE
             end

             else begin
                     read(ch);
                     c := ord(ch)
             end;
             getc := c
     end;

类型“字”是不一样的“字符”一样,因为ENDFILE也许NEWLINE不是一个“字符”变量的合法值。
目前并没有获得除命名(有效)的逻辑中的“程序”声明,每个程序单元号开始预定义文件到文件系统中的所有概念。这显然??反映了疾病预防控制中心批制度,最初是Pascal。文件变量

     var fv : file of type

是一种非常特殊的对象 - 它不能被分配??到,也只有通过使用内置的像“EOF”,“eoln”,“读”,“写”,“复位”和“重写”过程的调用。 (“重设”倒回一个文件,使它成为重读准备;'重写'使文件编写做好准备。)
Pascal的大多数实现提供了一个逃生舱口就允许名称文件从外部环境,而不是方便,而不是standardly。例如,许多系统调用允许在文件名参数“复位”和“重写”:

     reset(fv, filename);

但是,“复位”和“改写”是过程,不是函数 - 有没有地位,没有办法返回重新获得控制,如果由于某种原因,试图访问失败。 (UCSD提供了编译时间标志,禁用正常中止。)而且,由于FV的不能在表达式中出现像
    
     reset(fv, filename);
     if fv = failure then ...

有没有朝这个方向逃跑或者。这基本上是不可能的束缚使得编写程序,从错误中恢复的文件名拼写,等我从来没有解决的“工具”修订它充分。
没有获得命令行参数,可能再次反映Pascal的批处理起源的概念。本地程序可以允许添加非的标准程序的环境中。

既然是不可能写在Pascal一般用途的存储分配器(那里是没有办法谈的类型,这样的函数将返回),语言有一个从一个内置的所谓“新”的过程,分配空间堆。只有定义类型可以分配,所以它是不可能的分配,例如,任意大小的数组来保存字符串。由“新”返回的指针可能被传递而不是操纵:没有指针运算。有没有办法重新获得控制权,如果储存耗尽。

新标准提供了在这些领域没有任何变化。

5. 修饰问题

这些问题大多是有经验的程序员厌烦,一些可能还会滋扰初学者。它们都可以连在一起。
Pascal,与大多数其他以Algol为灵感的语言一样,使用分号而不是结束(在PL/I和C)作为语句分隔。因此必须有一个什么样的声明是把复杂的概念,合理妥善分号。也许更重要的是,如果一个人大约在适当的地方使用它们严重,相当数量的滋扰编辑需要。考虑第一个程序片段:

     if a then
             b;
     c;

但如果事情必须在b之前插入,它不再需要一个分号,因为它现在前面一个“结束”:

     if a then begin
             b0;
             b
     end;
     c;

现在,如果我们添加一个'else',就必须消除对'end'的分号:

     if a then begin
             b0;
             b
     end
     else
             d;
     c;

以及等等等等用分号上下翻飞的方案来进行下去。

一个普遍接受的程序员心理学实验的结果是,分号作为分隔约10倍更容易比分号作为结束符错误(16)(在Ada,(17)最重要的语言基础上Pascal,分号结束符。)幸运的是,Pascal可以几乎总是闭一只眼,并逃脱分号作为结束符。例外的是在像声明,其中与终端分离问题似乎并不严重反正地方,就在“其他”,这是很容易记住。

C和Ratfor程序员发现'begin'和'end'笨重相比,{和}。

一个函数名本身是该函数调用,而没有办法区分从一个简单的变量除非知道该函数的名称,如函数调用。Pascal采用了具有像在函数变量函数的名称的行为Fortran的把戏,但如在Fortran函数的名字真的是一个变量,并可以出现在表达式中,在Pascal,其表达式的外观是一个递归调用:如果f是一个零参数的函数,'F:= F +1为F的递归调用

有一个经营者缺乏(可能涉及到优先级的缺乏)。特别是,有没有位操作运算符(AND,OR,XOR等)。我只是放弃了试图写下面的Pascal中琐碎的加密程序:

     i := 1;
     while getc(c) <> ENDFILE do begin
             putc(xor(c, key[i]));
             i := i mod keylen + 1
     end

因为我不能写一个合理的“异或”的功能。设置的类型有点帮助这里(这么说),但还不够,声称Pascal是一个系统编程语言的人一般都忽略了这一点。例如,[18,第685]``Pascal是目前[1977]系统编程和软件实现最好的语言,似乎有点天真。
有没有空字符串,也许是因为Pascal使用翻了一番引用符号来表示字符串中嵌入一个报价:

     'This is a '' character'

有没有办法投入字符串非图形符号。事实上,非图形字符在unpersons感更强,因为它们不属于任何标准语言的一部分提及。如换行符,制表符等概念的处理系统在每一个“专案”的方式,通常是通过了解有关字符集(如ASCII换行符的十进制值10)的东西。

There is no macro processor.  The 'const' mechanism for defining manifest constants takes care of about 95 percent of the uses of simple #define statements in C, but more involved ones are hopeless.  It is certainly possible to put a macro preprocessor on a Pascal compiler.  This allowed me to simulate a sensible 'error' procedure as

     #define error(s) begin writeln(s); halt end

('halt' in turn might be defined as a branch to the end of the outermost block.) Then calls like

     error('little string');
     error('much bigger string');

work since 'writeln' (as part of the standard Pascal environment) can handle strings of any size.  It is unfortunate that there is no way to make this convenience available to routines in general.
The language prohibits expressions in declarations, so it is not possible to write things like

      const   SIZE = 10;
      type    arr = array [1..SIZE+1] of integer;

或者简单的就像

      const   SIZE = 10;
              SIZE1 = SIZE + 1;

6. 观点

在努力重写《软件工具》一书的程序从1980年3月开始,间歇的持续到1981年1月。最终此书(19)在1981年6月发布。那段时间我逐渐适应了与Pascal(修饰,控制流不足)表面的问题,并并发着不完善的解决方案(数组的大小,运行时环境)来使它好用。

要完成书里的程序,还要有良好的设计,是个不平凡的任务。但他们没有被有效的,也不是非常复杂的操作系统及其相互作用,使我能够得到一些漂亮的杂牌解决方案,那些根本就没有真正的解决方案。

没有明显的地方显示出Pascal优于C,但也有它是一个多Ratfor明显改善的几个地方。迄今为止最明显的就是递归:几个节目更清洁的书面递归时,特别是模式搜索,快速排序,评价和表达。

枚举数据类型是一个好主意。同时,他们划定的合法值的范围和文档中。记录有助于组相关的变量。我发现使用相对较少的指针。

布尔变量是整数比布尔条件更好;原Ratfor方案中有些不自然,因为Fortran的建设的逻辑变量的设计不当。

偶尔Pascal的类型检查会警告的手滑书面方案;价值观的运行时检查还表示不时的错误,特别是下标范围的行为。

至于消极的一面,重新编译一个从无到有的大型计划,以改变源一行是非常烦人;独立编译或没有类型检查,是对大型程序的强制性。

我来自一个事实,字符被部分Pascal和Fortran的部分不小的好处,因为字符串和非图形Pascal治疗等不足。在这两种语言,这是令人吃惊笨拙初始化的关键字,错误信息,如表和文字字符串。

完成的项目是在对他们的Ratfor等同的源代码行数相同的一般。起初这让我很吃惊,因为我的偏见是,Pascal是一个冗长,少表达语言。真正的原因似乎是在像Pascal许可证限制并在Fortran的循环(即,便携式的Fortran 66)没有,所以一些无用的分配可以消除标地任意表达式;此外,Ratfor程序申报功能,而Pascal的做没有。

临结束,让我总结一下反对Pascal的要点。

1.由于数组的大小是其类型的一部分​​,它是不可能写出通用例程,也就是要处理不同大小的阵列。特别是,字符串处理是非常困难的。
变量需要更多的范围比他们应该 -

2.在静态变量的初始化和通信方式的非层次相结合,破坏当地的`` ''程序缺乏。
各申报散射程序组件,逻辑上属于一起执行分开,将语言的力量的过程和函数是在一个不自然顺序排列一通的性质。

3.单独编制的缺乏阻碍了大型项目的发展,而不能使用的库。
逻辑表达式的计算顺序不能得到控制,从而导致复杂的代码和随机变量。

6.在“案例”的说法是被阉割的,因为没有违约条款。

7.标准I/O是有缺陷的。没有与文件或程序参数作为标准语言的一部分,并没有合理的处理提供扩展机制。

8.语言缺乏用于装配大型程序所需的工具最多,最显着的文件包含。

9.没有逃脱。

最后这一点也许是最重要的。语言是不足,但界限清楚,因为没有办法逃脱它的局限性。有没有转换禁用类型检查时必需的。有没有办法更换明智的做法有缺陷的运行时环境,除非控制编译器定义了``标准程序。''的语言是封闭的。
人们谁使用严重的编程陷入致命的陷阱Pascal。

因为语言是如此的无能,它必须扩展。但各组扩展了自己的方向Pascal,使它看起来像任何一种语言,他们真正想要的。单独编译,Fortran语言类常见,字符串数据类型,内部静态变量的初始化,八进制数,位域等扩展,都增加了语言的工具为一组,而破坏其移植性性。

我觉得做超出Pascal原定目标的事是一种错误。在它的形式看来,Pascal是一个玩具语言,适合教学,但不适合真正的编程。

致谢

I am grateful to Al Aho, Al Feuer, Narain Gehani, Bob Martin, Doug McIlroy, Rob Pike, Dennis Ritchie, Chris Van Wyk and Charles Wetherell for helpful criticisms of earlier versions of this paper.

[1] Feuer, A. R. and N. H. Gehani, ``A Comparison of the Programming Languages C and Pascal - Part I: Language Concepts,'' Bell Labs internal memorandum (September 1979).

[2] N. H. Gehani and A. R. Feuer, ``A Comparison of the Programming Languages C and Pascal - Part II: Program Properties and Programming Domains,'' Bell Labs internal memorandum (February 1980).

[3] P. Mateti, ``Pascal versus C: A Subjective Comparison,'' Language Design and Programming Methodology Symposium, Springer-Verlag, Sydney, Australia (September 1979).

[4] A. Springer, ``A Comparison of Language C and Pascal,'' IBM Technical Report G320-2128, Cambridge Scientific Center (August 1979).

[5] B. W. Kernighan and P. J. Plauger, Software Tools, Addison-Wesley, Reading, Mass. (1976).

[6] K. Jensen, Pascal User Manual and Report, Springer-Verlag (1978). (2nd edition.)

[7] David V. Moffat, ``A Categorized Pascal Bibliography,'' SIGPLAN Notices 15(10), pp. 63-75 (October 1980).

[8] A. N. Habermann, ``Critical Comments on the Programming Language Pascal,'' Acta Informatica 3, pp. 47-57 (1973).

[9] O. Lecarme and P. Desjardins, ``More Comments on the Programming Language Pascal,'' Acta Informatica 4, pp. 231-243 (1975).

[10] H. J. Boom and E. DeJong, ``A Critical Comparison of Several Programming Language Implementations,'' Software Practice and Experience 10(6), pp. 435-473 (June 1980).

[11] N. Wirth, ``An Assessment of the Programming Language Pascal,'' IEEE Transactions on Software Engineering SE-1(2), pp. 192-198 (June, 1975).

[12] O. Lecarme and P. Desjardins, ibid, p. 239.

[13] A. M. Addyman, ``A Draft Proposal for Pascal,'' SIGPLAN Notices 15(4), pp. 1-66 (April 1980).

[14] J. Welsh, W. J. Sneeringer, and C. A. R. Hoare, ``Ambiguities and Insecurities in Pascal,'' Software Practice and Experience 7, pp. 685-696 (1977).

[15] N. Wirth, ibid., p. 196.

[16] J. D. Gannon and J. J. Horning, ``Language Design for Programming Reliability,'' IEEE Trans. Software Engineering SE-1(2), pp. 179-191 (June 1975).

[17] J. D. Ichbiah, et al, ``Rationale for the Design of the Ada Programming Language,'' SIGPLAN Notices 14(6) (June 1979).

[18] J. Welsh, W. J. Sneeringer, and C. A. R. Hoare, ibid.

[19] B. W. Kernighan and P. J. Plauger, Software Tools in Pascal, Addison-Wesley (1981).

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WINDOWS环境 Windows几乎不需要介绍。然而人们很容易忘记Windows给办公室和家庭桌上型计算机所带来的重大改变。Windows在其早期曾经走过一段坎坷的道路,征服桌上型计算机市场的前途一度相当渺茫。 Windows简史 在1981年秋天IBM PC推出之后不久,MS-DOS就已经很明显成为PC上的主流操作系统。MS-DOS代表Microsoft Disk Operating System(磁盘操作系统)。MS-DOS是一个小型的操作系统。MS-DOS提供给用户一种命令列接口,提供如DIR和TYPE的命令,也可以将应用程序加载内存执行。对于应用程序写作者,它提供了一组函数呼叫,进行文件的输入输出(I/O )。对于其它的外围处理-尤其是将文字或图形写到显示器上-应用程序可以直接存取PC的硬件。 由于内存和硬件的限制,成熟的图形环境缓慢地才到来。当苹果计算机公司不幸的Lisa计算机在1983年1月发表时,它提供了不同于文字模式环境的另一种选择,并在1984年1月成为Macintosh上图形环境的一种标准。尽管Macintosh的市场占有率在下降,但是它仍然被认为是衡量所有其它图形环境的标准。包括Macintosh和Windows的所有图形环境,其实都要归功于Xerox Palo Alto Research Center(PARC)在70年代中期所作的开拓性研究工作。 Windows是由微软在1983年11月(在Lisa之后,Macintosh之前)宣布,并在两年后(1985年11月)发行。在此后的两年中,紧随着Microsoft Windows早期版本1.0之后,又推出了几种改进版本,以支持国际商业市场,并提供新型视讯显示器和打印机的驱动程序。 Windows版本2.0是在1987年11月正式在市场上推出的。该版本对使用者接口做了一些改进。这些改进中最有效的是使用了可重迭式窗口,而Windows 1.0中使用的是并排式窗口。Windows 2.0还增强了键盘和鼠标接口,特别是加入了菜单和对话框。 至此,Windows还只要求Intel 8086或者8088等级的微处理器,以「实际模式」执行,只能存取地址在1MB以下的内存。Windows/386(在Windows 2.0之后不久发行的)使用Intel 386微处理器的「虚拟8086」模式,实现将直接存取硬件的多个MS-DOS程序窗口化和多任务化。为了统一起见,Windows版本2.1被更名为Windows/286。 Windows 3.0是在1990年5月22日发表的。它将Windows/286和Windows/386结合到同一种产品中。Windows 3.0有了一个很大的改变,这就是对Intel的286、386和486微处理器保护模式的支持。这能使Windows和Windows应用程序能存取高达16MB的内存。Windows用于执行程序和维护文件的「外壳」程序得到了全面的改进。Windows 3.0是第一个在家用和办公室市场上取得立足点的版本。 任何Windows的历史介绍都必须包括一些OS/2的说明,OS/2是对DOS和Windows的另一种选择,最初是由Microsoft和IBM合作开发的。OS/2版本1.0(只有文字模式)在Intel 286(或者后来的)微处理器上运行,在1987年末发布。在1988年10月的OS/2版本1.1中出现了管理图形使用者接口的PM(Presentation Manager)。PM最初的设计构想是成为Windows的一种保护模式版本,但是图形API改变程度太大,致使软件生产厂商很难提供对这两种平台的支持。 到1990年9月,IBM和Microsoft之间的冲突达到了高峰,导致这两个公司最后分道扬镳。IBM接管了OS/2,而Microsoft明确表示Windows将是他们操作系统策略的中心。虽然OS/2仍然拥有一些狂热的崇拜者,但是它远不及Windows这样的普及程度。 Microsoft Windows版本3.1是1992年4月发布的,其中包括的几个重要特性是TrueType字体技术(给Windows带来可缩放的轮廓字体)、多媒体(声音和音乐)、对象连结和嵌入(OLE:Object Linking and Embedding)和通用对话框。跟OS/2一样,Windows 3.1只能在保护模式下运作,并且要求至少配置了1MB内存的286或386处理器。 在1993年7月发表的Windows NT是第一个支持Intel 386、486和Pentium微处理器32位保护模式的Windows版本。Windows NT提供32位平坦寻址,并使用32位的指令集。(本章后面我会谈到一些寻址空间的问题)。Windows NT还可以移植到非Intel处理器上,并在几种使用RISC芯片的工作站上执行。 Windows 95是在1995年8月发布的。和Windows NT一样,Windows 95也支持Intel 386或更高等级处理器的32位保护模式。虽然它缺少Windows NT中的某些功能,诸如高安全性和对RISC机器的可移植性等,但是Windows 95具有需要较少硬件资源的优点。 Windows 98在1998年6月发布,具有许多加强功能,包括执行效能的提高、更好的硬件支持以及与因特网和全球信息网(WWW)更紧密的结合。 Windows方面 Windows 98和Windows NT都是支持32位优先权式多任务(preemptive multitasking)及多线程的图形操作系统。Windows拥有图形使用者接口(GUI ),这种使用者界面也称作「可视化接口」或「图形窗口环境」。有关GUI的概念可追溯至70年代中期,在Alto和Star等机器上以及SmallTalk等环境中由Xerox PARC所作的研究工作。该项研究的成果后来被Apple Computer和Microsoft引入主流并流行起来。虽然有一些争议,但现在已非常清楚,GUI是(Microsoft的Charles Simonyi的说法)一个在个人计算机工业史上集各方面技术大成于一体的最重要产物。 所有GUI都在点矩阵对应的视讯显示器上处理图形。图形提供了使用屏幕的最佳方式、传递信息的可视化丰富多彩环境,以及能够WYSIWYG(what you see is what you get:所见即所得)的图形视讯显示和为书面文件准备好格式化文字输出内容。 在早期,视讯显示器仅用于响应使用者通过键盘输入的文字。在图形使用者接口中,视讯显示器自身成为使用者输入的一个来源。视讯显示器以图标和输入设备(例如按钮和滚动条)的形式显示多种图形对象。使用者可以使用键盘(或者更直接地使用鼠标等指向设备)直接在屏幕上操纵这些对象,拖动图形对象、按下鼠标按钮以及滚动滚动条。 因此,使用者与程序的交流变得更为亲密。这不再是一种从键盘到程序,再到视讯显示器的单向信息流动,使用者已经能够与显示器上的对象直接交互作用了。 使用者不再需要花费长时间学习如何使用计算机或掌握新程序了。Windows让这一切成真,因为所有应用程序都有相同的基本外观和感觉。程序占据一个窗口-屏幕上的一块矩形区域。每个窗口由一个标题列标识。大多数程序功能由程序的菜单开始。用户可使用滚动条观察那些无法在一个屏幕中装下的信息。某些菜单项目触发对话框,用户可在其中输入额外的信息。几乎在每个大的Windows程序中都有一个用于开启文件的特殊对话框。该对话框在所有这些Windows程序中看起来都一样(或接近相同),而且几乎总是从同一菜单选项中启动。 一旦您了解使用一个Windows程序的方法,您就非常容易学习其它的Windows程序。菜单和对话框允许用户试验一个新程序并探究它的功能。大多数Windows程序同时具有键盘接口和鼠标接口。虽然Windows程序的大多数功能可通过键盘控制,但使用鼠标要容易得多。 从程序写作者的角度看,一致的使用者接口来自于Windows建构菜单和对话框的内置程序。所有菜单都有同样的键盘和鼠标接口,因为这项工作是由Windows处理,而不是由应用程序处理。 为便于多个程序的使用,以及这些程序间信息的交换,Windows支持多任务。在同一时刻能有多个Windows程序显示并运行。每个程序在屏幕上占据一个窗口。用户可在屏幕上移动窗口,改变它们的大小,在不同程序间切换,并从一个程序向另一个程序传送数据。因为这些窗口看起来有些像桌面上的纸(当然,这是计算机还未占据办公桌之前的年代),Windows有时被称作:一个显示多个程序的「具象化桌面」。 Windows的早期版本使用一种「非优先权式(non-preemptive)」的多任务系统。这意味着Windows不使用系统定时器将处理时间分配给系统中运行的多个应用程序,程序必须自愿放弃控制以便其它程序运行。在Windows NT和Windows 98中,多任务是优先权式的,而且程序自身可分割成近乎同时执行的多个执行绪。 操作系统不对内存进行管理便无法实现多任务。当新程序启动、旧程序终止时,内存会出现碎裂空间。系统必须能够将闲置的内存空间组织在一起,因此系统必须能够移动内存中的程序代码和数据块。 即使是在8088微处理器上跑的Windows 1.0也能进行这类内存管理。在实际模式限制下,这种能力被认为是软件工程一个令人惊讶的成就。在Windows 1.0中,PC硬件结构的640KB内存限制,在不要求任何额外内存的情况下被有效地扩展了。但Microsoft并未就此停步:Windows 2.0允许Windows应用程序存取扩充内存(EMS);Windows 3.0在保护模式下,允许Windows应用程序存取高达16MB的扩展内存。Windows NT和Windows 98通过成熟的32位操作系统及平坦寻址空间,摆脱了这些旧的限制。 Windows上执行的程序可共享在称为「动态链接库」的文件中的例程。Windows包括一个机制,能够在执行时连结使用动态链接库中例程的程序。Windows自身基本上就是一个动态链接库的集合。 Windows是一个图形接口,Windows程序能够在视讯显示器和打印机上充分利用图形和格式化文字。图形接口不仅在外观上更有吸引力,而且还能够让使用者传递高层次的信息。 Windows应用程序不能直接存取屏幕和打印机等图形显示设备硬件。相反,Windows提供一种图形程序语言(称作图形设备接口,或者GDI),使显示图形和格式化文字更容易。Windows虚拟化了显示硬件,使为Windows编写的程序可使用任何具有Windows设备驱动程序的视频卡或打印机,而程序无需确定系统相连的设备类型。 对Windows开发者来说,将与设备无关的图形接口输出到IBM PC上不是件轻松的事。PC的设计是基于开放式架构的原则,鼓励第三方硬件制造商为PC开发接口设备,而且开发了大量这样的设备。虽然出现了多种标准,PC上的传统MS-DOS程序仍不得不各自支持许多不同的硬设备。这对MS-DOS字处理软件来说非常普遍,它们连同1到2张有许多小文件的磁盘一同销售,每个文件支持一种特定的打印机。Windows程序不要求每个应用程序都自行开发这些驱动程序,因为这种支持是Windows的一部分。 动态链接 Windows运作机制的核心是一个称作「动态链接」的概念。Windows提供了应用程序丰富的可呼叫函数,大多数用于实作其使用者接口和在视讯显示器上显示文字和图形。这些函数采用动态链接库(Dynamic Linking Library,DLL)的方式撰写。这些动态链接库是些具有.DLL或者有时是.EXE扩展名的文件,在Windows 98中通常位于\WINDOWS\SYSTEM子目录中,在Windows NT中通常位于\WINNT\SYSTEM和\WINNT\SYSTEM32子目录中。 在早期,Windows的主要部分仅通过三个动态链接库实作。这代表了Windows的三个主要子系统,它们被称作Kernel、User和GDI。当子系统的数目在Windows最近版本中增多时,大多数典型的Windows程序产生的函数呼叫仍对应到这三个模块之一。Kernel(日前由16位的KRNL386.EXE和32位的KERNEL32.DLL实现)处理所有在传统上由操作系统核心处理的事务-内存管理、文件I/O和多任务管理。User(由16位的USER.EXE和32位的USER32.DLL实作)指使用者接口,实作所有窗口运作机制。GDI(由16位的GDI.EXE和32位的GDI32.DLL实作)是一个图形设备接口,允许程序在屏幕和打印机上显示文字和图形。 Windows 98支持应用程序可使用的上千种函数呼叫。每个函数都有一个描述名称,例如CreateWindow。该函数(如您所猜想的)为程序建立新窗口。所有应用程序可以使用的Windows函数都在表头文件里预先声明过。 在Windows程序中,使用Windows函数的方式通常与使用如strlen等C语言链接库函数的方式相同。主要的区别在于C语言链接库函数的机械码连结到您的程序代码中,而Windows函数的程序代码在您程序执行文件外的DLL中。 当您执行Windows程序时,它通过一个称作「动态链接」的过程与Windows相接。一个Windows的.EXE文件中有使用到的不同动态链接库的参考数据,所使用的函数即在那些动态链接库中。当Windows程序被加载到内存中时,程序中的呼叫被指向DLL函数的入口。如果该DLL不在内存中,就把它加载到内存中。 当您连结Windows程序以产生一个可执行文件时,您必须连结程序开发环境提供的特定「引用链接库(import library)」。这些引用链接库包含了动态链接库名称和所有Windows函数呼叫的引用信息。连结程序使用该信息在.EXE文件中建立一个表格,在加载程序时,Windows使用它将呼叫转换为Windows函数。 WINDOWS程序设计选项 为说明Windows程序设计的多种技术,本书提供了许多范例程序。这些程序使用C语言撰写并原原本本的使用Windows API来开发程序。我将这种方法称作「古典」Windows程序设计。这是我们在1985年为Windows 1.0写程序的方法,它今天仍是写作Windows程序的有效方法。 API和内存模式 对于程序写作者来说,操作系统是由本身的API定义的。API包含了所有应用程序能够使用的操作系统函数呼叫,同时包含了相关的数据型态和结构。在Windows中,API还意味着一个特殊的程序架构,我们将在每章的开头进行研究。 一般而言,Windows API自Windows 1.0以来一直保持一致,没什么重大改变。具有Windows 98程序写作经验的Windows程序写作者会对Windows 1.0程序的原始码感觉非常熟悉。API改变的一种方式是进行增强。Windows 1.0支持不到450个函数呼叫,现在已有了上千种函数呼叫。 Windows API和它的语法的最大变化来自于从16位架构向32位架构转化的过程中。Windows从版本1.0到版本3.1使用16位Intel 8086、8088、和286微处理器上所谓的分段内存模式,由于兼容性的原因,从386开始的32位Intel微处理器也支持该模式。在这种模式下,微处理器缓存器的大小为16位,因此C的int数据型态也是16位宽。在分段内存模式下,内存地址由两个部分组成-一个16位段(segment)指针和一个16位偏移量(offset)指标。从程序写作者的角度看,这非常凌乱并带来了long或far指针(包括段地址和偏移量地址)和short或near指标(包括带有假定段地址的偏移量地址)的区别。 从Windows NT和Windows 95开始,Windows支持使用Intel 386、486和Pentium处理器32位模式下的32位平坦寻址内存模式。C语言的int数据型态也扩展为32位的值。为32位版本Windows编写的程序使用简单的平坦线性空间寻址的32位指针值。 用于16位版本Windows的API(Windows 1.0到Windows 3.1)现在称作Win16。用于32位版本Windows的API(Windows 95、Windows 98和所有版本的Windows NT)现在称作Win32。许多函数呼叫在从Win16到Win32的转变中保持相同,但有些需要增强。例如,图像坐标点由Win16中的16位值变为Win32中的32位值。此外,某些Win16函数呼叫返回一个包含在32位整数值中的二维坐标点。这在Win32中不可能,因此增加的新函数呼叫以不同方式运作。 所有32位版本的Windows都支持Win16 API(以确保和旧有应用程序兼容)和Win32 API(以运行新应用程序)。非常有趣的是,Windows NT与Windows 95及Windows 98的工作方式不同。在Windows NT中,Win16函数呼叫通过一个转换层被转化为Win32函数呼叫,然后被操作系统处理。在Windows 95和Windows 98中,该操作正相反:Win32函数呼叫通过转换层转换为Win16函数呼叫,再由操作系统处理。 在同一时刻有两个不同的Windows API集(至少名称不同)。Win32s (「s」代表「subset(子集)」)是一个API,允许程序写作者编写在Windows 3.1上执行的32位应用程序。该API仅支持已被Win16支持的32位函数版本。此外,Windows 95 API一度被称作Win32c(「c」代表「compatibility(兼容性)」),但该术语已被抛弃了。 现在,Windows NT和Windows 98都被认为能够支持Win32 API。然而,每个操作系统依然都支持某些不被别的操作系统支持的某些功能特性。因为它们的相同之处是相当可观的,所以有可能编写在两个操作系统下都可执行的程序。而且,人们普遍认为这两个产品最终会合而为一。 语言选项 使用C语言和原始的API不是编写Windows 98程序的唯一方法。然而,这种方法却提供给您最佳的性能、最强大的功能和在发掘Windows特性方面最大的灵活性。可执行文件相对较小且运行时不要求外部链接库(自然,Windows DLL自身除外)。最重要的是,不管您最终以什么方式开发Windows应用程序,熟悉API会使您对Windows内部有更深入的了解。 虽然我认为学习古典的Windows程序设计对任何Windows程序写作者都是重要的,我没有必要建议使用C和API编写每个Windows应用程序。许多程序写作者,特别是那些为公司内部开发程序或在家编写娱乐程序的程序写作者喜欢轻松的开发环境,例如Microsoft Visual Basic或者Borland Delphi(它结合了对象导向的Pascal版本)。这些环境使程序写作者将精力集中于应用程序的使用者接口和相关使用者接口对象的程序代码上。要学习Visual Basic,您也许需要参考Microsoft Press的一些其它图书,例如Michael Halvorson1996年着的《Learn Visual Basic Now》。 在专业程序写作者中-特别是那些开发商业应用程序的程序写作者-Microsoft Visual C++和Microsoft Foundation Class Library(MFC)是近年来流行的选择。MFC在一组C++对象类别中封装了许多Windows程序设计中的琐碎细节。Jeff Prosise的《Programming Windows with MFC,第二版》(Microsoft Press,1999年)提供了MFC程序的写作指南。 最近,Internet和World Wide Web的流行大力推广着Sun Microsystems的Java,这是一个受C++启发却与微处理器无关的程序设计语言,而且结合了可在几个操作系统平台上执行的图形应用程序开发工具组。Microsoft Press有一本关于Microsoft J++(Microsoft的Java)开发工具的好书,《Programming Visual J++ 6.0》(1998年),由Stephen R. Davis着。 显然,很难说哪种方法更有利于开发Windows应用程序。更主要的是,也许是应用程序自身的特性决定了所使用的工具。不管您最后实际上使用什么工具写作程序,学习Windows API将使您更深入地了解Windows工作的方式。Windows是一个复杂的系统,在API上增加一个程序写作层并未减少它的复杂性,仅仅是掩盖了它,早晚您会碰到它。了解API会给您更好的补救机会。 在原始的Windows API之上的任何软件层都必定将您限制在全部功能的一个子集内。您也许发现,例如,使用Visual Basic编写应用程序非常理想,然而它不允许您做一个或两个很简单的基本工作。在这种情况下,您将不得不使用原始的API呼叫。API定义了作为Windows程序写作者所需的一切。没有什么方法比直接使用API更万能的了。 MFC尤其问题百出。虽然它大幅简化了某些工作(例如OLE),我却经常发现要让它们按我所想的去工作时,会在其它特性(例如Document/View架构)上碰壁。MFC还不是Windows程序设计者所追求的灵丹妙药,很少有人认为它是一个好的对象导向设计的模型。MFC程序写作者从他们使用的对象类别定义如何工作中受益颇深,并会发现他们经常参考MFC原始码,搞懂这些原始码是学习Windows API的好处之一。 程序开发环境 在本书中,假定您正使用Microsoft Visual C++ 6.0,标准版、专业版和企业版都可以。经济的标准版足以应付本书中的程序设计需求。Visual C++ 还是Visual Studio 6.0中的一部分。 Microsoft Visual C++ 软件包中包括C编译器和其它编译及连结Windows程序所需的文件和工具等。它还包括Visual C++ Developer Studio,一个可编辑原始码、以交谈方式建立资源(如图标和对话框)以及编辑、编译、执行和测试程序的环境。 如果您正使用Visual C++ 5.0,则需要为Windows 98和Windows NT 5.0更新表头文件和引用链接库,这些东西可从Microsoft的网站上得到。在 http://www.microsoft.com/msdn/,选择「Downloads」,然后选择「 Platform SDK」(软件开发套件),您就能在选择的目录中下载和安装更新文件。要让Microsoft Developer Studio浏览这些目录,可以从「Tool」菜单项选择「 Options」然后按下「Directories」标签。 Microsoft网站上的msdn部分代表「Microsoft Developer Network(Microsoft软件开发者网络)」。这是一个向程序写作者提供了经常更新的CD-ROM的计划,这些CD-ROM中包含了程序写作者在Windows开发中所需的最新东西。您也可以订阅MSDN,这样就避免经常得从Microsoft的网站下载文件。 API文件 本书不是Windows API权威的正式文件的替代品。那组文件不再以印刷形式出版,它仅能从CD-ROM或Internet上取得。 当您安装Visual C++ 6.0时,您将得到一个包括API文件的在线求助系统。您可通过订阅MSDN或使用Microsoft网站上的在线求助系统更新该文件。连接到 http://www.microsoft.com/msdn/,并选择「MSDN Library Online」。 在Visual C++ 6.0中,从「Help」菜单项选择「Contents」项目开启MSDN窗口。API文件按树形结构组织,寻找标有「 Platform SDK」的部分,所有在本书中引用的文件都来自于该部分。我将向您介绍如何从「 Platform SDK」开始寻找以斜线分层分门别类的文件的位置。(我知道「Platform SDK」是整个MSDN知识库中较为晦涩的部分,但我敢保证那是Windows程序设计的基本核心。)例如,对于如何在Windows程序中使用鼠标的文件,您可参考/ Platform SDK / User Interface Services / User Input / Mouse Input。 我在前面提到Windows大致分为Kernel、User和GDI子系统。kernel接口在/ Platform SDK / Windows Base Services中,User界面函数在 / Platform SDK / User Interface Services中,GDI位于 / Platform SDK / Graphics and Multimedia Services / GDI中。 编写第一个WINDOWS程序 现在是开始写些程序的时候了。为了便于对比,让我们以一个非常短的Windows程序和一个简短的文字模式程序开始。这会帮助我们找到使用开发环境并感受建立和编译程序机制的正确方向。 文字模式(Character-Mode)模型 程序写作者们喜爱的一本书是《The C Programming Language》(Prentice Hall,1978年和1988年),由Brian W. Kernighan和Dennis M. Ritchie(亲切地称为K&R)编着。该书的第一章以一个显示「hello, world」的C语言程序开始。 这里是在《The C Programming Language》第一版第6页中出现的程序: main () { printf ("hello, world\n") ; } 以前C程序写作者在使用printf等C执行期链接库函数时,无需先声明它们。但这是90年代,我们愿意给编译器一个在我们的程序中标出错误的机会。这里是在K&R第二版中修正的程序: #include <stdio.h> main () { printf ("hello, world\n") ; } 该程序仍然是那么短。但它可通过编译并执行得很好,但当今许多程序写作者更愿意清楚地说明main函数的返回值,在这种情况下ANSI C规定该函数必须返回一个值: #include <stdio.h> int main () { printf ("hello, world\n") ; return 0 ; } 我们还可以包括main的参数,把程序弄得更长一些,但让我们暂且这样就好了-包括一个include声明、程序的进入点、一个对执行期链接库函数的呼叫和一个return语句。 同样效果的Windows程序 Windows关于「hello, world」程序的等价程序有和文字模式版本完全相同的组件。它有一个include声明、一个程序进入点、一个函数呼叫和一个return语句。下面便是该程序: /*------------------------------------------------------------------ HelloMsg.c -- Displays "Hello, Windows 98!" in a message box (c) Charles Petzold, 1998 --------------------------------------------------------------------*/ #include <windows.h> int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { MessageBox (NULL, TEXT ("Hello, Windows 98!"), TEXT ("HelloMsg"), 0); return 0 ; } 在剖析该程序之前,让我们看一下在Visual C++ Developer Studio中建立新程序的方式。 首先,从File菜单中选New。在 New对话框中,单击Projects页面标签,选择 Win32 Application。在Location栏中,选择一个子目录,在 Project Name栏中,输入该项目的名称,此时该名称是HelloMsg,这便是在 Location栏中显示的目录的子目录。Create New Workspace复选框应该勾起来,Platforms部分应该显示 Win32,选择OK。 将会出现一个标题为Win32 Application - Step 1 Of 1的对话框,指出要建立一个Empty Project,并按下Finish按钮。 从File菜单中再次选择New。在 New对话框中,选择Files页面标签,选择 C++ Source File。Add To Project复选框应被选中,并应显示HelloMsg。在 File Name栏中输入HelloMsg.c,选中OK。 现在您可输入上面所示的HELLOMSG.C文件,您也可以选择Insert菜单和 File As Text选项从本书附带的CD-ROM上复制HELLOMSG.C的内容。 从结构上说,HELLOMSG.C与K&R的「hello,world」程序是相同的。表头文件STDIO.H已被WINDOWS.H所代替,进入点main被WinMain所代替,而且C语言执行时期链接库函数printf被Windows API函数MessageBox所代替。然而,在程序中有许多新东西,包括几个陌生的大写标识符。 让我们从头开始。 表头文件 HELLOMSG.C以一个前置处理器指示命令开始,实际上在每个用C编写的Windows程序的开头都可看到:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值