请问什么是LPC

请问什么是LPC,第1张

第一章 Lpc程序和编程环境

-----------------------------------------------------------

第一节 编程环境

通常我们所见到的Mud大多是LpMud。LpMuds使用Unix的指令和文件结构。如果你对Unix有所了解,那么LpMud中的一些指令和它的文件结构与普通的Unix基本一样。如果你从未使用过Unix,那么它与Dos不同的是在文件的路径用"/",而不是Dos的"\".

一个典型的LpMud的文件是这样的:

/clone/player/player.c

"/clone/player/"是路径,player.c是文件名。

在多数的LpMud中,下面这些的基本的Unix指令是可以使用的:

pwd, cd, ls, rm, mv, cp, mkdir, rmdir, more, tail, cat, ed

如果从未使用过Unix,那么下面这张表也许是有用的。

pwd: 显示当前目录

cd: 改换你当前的工作目录,和Dos的cd一样。

ls: 列出指定目录下的所有文件,如果没有指定任何目录,那就列出当前目录底下的文件。和Dos的dir一样。

rm: 删除一个文件 和Dos的rmdir一样

mv: 从命名一个文件 和Dos的move一样

cp: 复制一个文件 和Dos的copy一样

mkdir: 创建一个目录

rmdir: 删除一个目录

more: 按页显示一个文件在你的当前屏幕。

cat: 显示整个文件。和Dos的type一样。

tail: 显示一个文件的结尾几行。

ed: 允许你使用Mud自带的编辑器,编辑一个文件。

-----------------------------------------------------------

第二节 Lpc程序

2.1 Lpc程序。

Lpc的程序看起来和一般的C区别不断大,语法基本一样,但是Lpc和一般的语言有着根本的不同,Lpc程序是编写一个一个的"Object"。这有什么区别呢?一般的程序是在执行过程中,通常有一个明显的开始和和结束。程序从一个地方开始,然后顺序执行下去,到了结束的地方就中断了。Lpc的Object不是这样的。

所谓的不同的Mud,实际上是一些不同的Lpc的Object在一个Driver的上的各种不同的表现。也就说,Lpc的Object是运行在一个Driver上的。这些Object组成了LpMud的丰富多彩的世界。Driver几乎不参与创建你所接触到的世界,它所完成的工作只是让那些Lpc的Object活动起来。Lpc的Object可能没有明显的开始和结束的标志,它可能

永远在工作。

和一般的程序一样,Lpc“程序”也是由一个或多个文件组成。一个Lpc的Object是按如下方式被执行的:Driver把和这个Object相关的文件读入内存,然后解释执行。但是要记住的是,读入内存,并不是说,它就开始按顺序执行。

2.2 Driver和Mudlib关系

在有些游戏中,整个游戏包括Driver和游戏世界都用C写好,这样能快一些,但是游戏的扩充性很差,巫师们不可能在游戏进行中添加任何东西。LpMud则相反。Driver理论上应该和玩家所接触的世界几乎没有任何直接的联系。游戏世界应该是自己独立的,而且是“即玩即加”的。这就是为什么LpMud使用Lpc作为编程语言的原因。它允许你创建一个游戏世界,再由Driver在需要时读入解释执行。Lpc甚至比C更简单,更容易明白,但是它可以创建一个可以让许多人在上面游戏的世界。

在你写完一个Lpc的文件时,它存在于主机的硬盘上。在游戏进行中,当需要整个Object时,这份文件将被调入内存,一个特殊的函数被调用来初始化这个Object的一些变量。现在你不用关心什么是变量,什么是函数以及游戏本身怎样来调用这个object,你只要记住Driver从硬盘中读入一个文件,然后放在内存中,如果没有任何

错误的话。

2.3 一个Object被装人内存。

一个Object不会也不必有一个特点的地方让Driver去执行它,通常Drvier会在Object中找一个地方去初始化它。一般都是这个函数叫做create()。

Lpc的Object是一些变量(它的值能变化)和函数(函数通常是用来 *** 纵那些变量的一段程序)的组合。函数 *** 纵变量的方式有:调用其他函数,使用Driver内部定义的函数(efun),基本的Lpc表达式以及流控制。

我们来看个变量的例子: wiz_level。这个变量记录你的巫师等级,如果是0呢,通常是普普通通的玩家了。这个值如果越大就表示你的巫师等级越高。这个也同时控制了你能不能执行一些巫师指令。基本上来说,一个Object就是一些变量“堆”在一起的东西。一个Object改变了,也就是某一个或者一些变量改变了。总的来说,一个Object如果要被内存中的另一个Object调用,Driver会去找这个Object的那堆变量放在哪里,如果这些变量没有值,那么Driver会调用一个特定的函数create来初始化这些变量。

但是create()不是Lpc代码开始执行的地方,只是大多数的Object从这里开始。事实上,create()可以不存在。如果这个Object不需要对变量初始化,那么create()可以不存在。那么这样的Object开始执行的地方就完全的不同于一般的Object,可以从任何地方开始。

那么究竟什么是Lpc的Object?Lpc的Object就是一堆变量的集合,它带有一个或者更多的函数来 *** 纵控制这些变量,函数的排列顺序是无所谓的,随便那个排在前面对这个Object的特性没有影响。

2.3 代码风格

在上面说过函数的顺序对这个Object的特性是毫无影响的。但是一个有着良好代码风格的程序对LpMud是很重要的。因为LpMud通常不会也不可能是一个人完成的,如果程序没有较好的可读性,那么别人理解你的“作品”是很困难的。而且有个良好的程序风格能给人以优雅的感觉,因此希望大家写的Lpc程序能有个好的风格。大家中的有些人可能以后会加入XO team创建自己梦想中的世界,我们要求你采用如下的格式书写程序。

2.3.1 头文件

在一个文件的开头是一段说明。采用如下格式:

/* /u/trill/obj/test.c

* from XO Object Library

* 测试用的Object

* created by trill 19970808

* version @(#) test.c 2.1@(#)

* last modified by trill 19971008

* 测试tell_wizard这个simul_efun

*/

第一行是这个文件的绝对路径,就是全路径。

第二行是它所在的Mudlib

第三行是它的功能的简单的描述,可以超过一行。

第四行是这个文件的作者和创建时间。

第五行是它的版本号,可能做了多次修改,甚至可能会重写,这个数字2.1标志了它大概做过多少次改动。

第六行是最后一次修改的人和时间。

第七行是最后一些修改什么东西。

对于一个Object我们要求必须有这样一段说明,特别是前面的五行必须存在,如果做了改动那么最后两行也要加上。这样一般的一个Object,我们从这段说明就能了解到一些很重要的信息。

下面是include一些文件和继承(inherit)一些Object。

#include <ansi.h>

#include "include/test.h"

inherit NPC

先系统的文件,后自己定义的一些头文件。特别要求的是必须有个和这个Object同名的".h"文件,比如"test.h"放在这个Object所在的目录的下一级目录"include"底下,就是说在include部分的最后一行是#include "include/test.h"。

在test.h定义所有在test.c用到的函数的原形,以及定义一些宏和常量。

这样做的好处是:

第一不用出现一个函数在引用时没有说明,

第二如果想知道这个Object有什么函数,直接看这个文件就可以了,不必去看那个test.c,可能test.c非常长。

第三如果建立一个help系统,用来查询每个Object存在的函数,那么这样直接去读test.h就可以,否则是一件很麻烦的事。

关于inherit我们在继承部分再说。

2.3.2 变量说明

在变量说明部分,大家最好在每个变量后面加一个简单的说明。

2.3.3 函数

一个Object的函数的顺序和名字对这个Object的表现是毫无影响的。但是为了让这个Object有良好的可读性,我们要求一个Object的函数按如下方式排列和命名:

首先是变量的接口部分,这些函数统一用Set+变量名来改变该变量的值,用Get+变量来返回变量的值。比如

static int level

void SetLevl(int i)

{

level = i

}

int GetLevel()

{

return level

}

其次是一些 *** 纵和控制变量的一些函数。比如

void AddLevel(int i)

{

一个Object的函数的顺序和名字对这个Object的表现是毫无影响的。但是为了让这个Object有良好的可读性,我们要求一个Object的函数按如下方式排列和命名:

首先是变量的接口部分,这些函数统一用Set+变量名来改变该变量的值,用Get+变量来返回变量的值。比如

static int level

void SetLevl(int i)

{

level = i

}

int GetLevel()

{

return level

}

其次是一些 *** 纵和控制变量的一些函数。比如

void AddLevel(int i)

{

level += i

}

这两类函数要求每个单词的第一字母大写。

再是一些Object所能做的事件(event),比如战斗,结婚等等。比如

void eventQuit()

{

...

}

这些函数要求事件的每个单词的第一字母大写,比如eventFight,eventMarry等等。

再下面的是由Driver调用的一些函数,比如create(), heart_beat,setup()。

最后是一些这个Object自己私有的函数,完成一些特别的功能。这些函数通常让要求每个单词的小写,中间用下划线(_)隔开。

要注意的是每个函数之间用一个空行隔开。

这些是对一个文件的整体要求,如果你有兴趣将来在XO team写程序,最好从现在开始就养成这样的编程习惯。如果你是别的Mud里面的巫师,我想一个Mud里面最好也有一个统一的整齐的风格。

也许你会问,这样要求有必要吗?这样太麻烦,程序写了自己能明白就可以了。这是不对的,LpMud是大家合作的项目,如果你做的程序别人没法看懂,不知道写的东西里面有些什么,能调用什么函数,那么实际上你写的东西是失败的,没人会去用它,它可能永远“死”在硬盘上。而且函数统一的命名法能尽快找到你所需要的函数,同时也能提高整个程序的可读性。

对于代码风格XO还有一些别的要求,我们将在以后的文章中介绍,如果你加入了XO team,代码风格将是第一篇要读的文章。

小结:

关于Lpc程序和编程环境,就介绍到这里。看完这一章,我想大家要记住的是LpMud是采用Lpc做为编程语言,Unix文件结构作为文件组织形式。Lpc是编写Object的一种语言,它的程序没有特殊的开始和结束的标志。如果Object被使用到,那么它被调入内存,如果这个Object有一个叫create()的函数,首先被执行,来初始化一些变量。

Lpc的Object是一堆变量的集合,同时带有一些能 *** 纵改变这些变量的函数。Lpc的代码风格,我想一个Mud最好有一个统一的风格,特别的XO有自己的特别的要求。

题外话:

当了好久的巫师,也用Lpc写了一些东西。我一直在试着理解Lpc,因为以我看如果一个巫师没有真正理解Lpc,他就不可能真正理解LpMud。理解Lpc并不仅仅意味着会使用它,许多巫师能使用它但是并不真正理解它。我希望在这个Lpc的介绍文章,能给大家一个Lpc的整体的印象,真正把握和理解Lpc,能创造自己心中梦想的世界。

-----------------------------------------------------------

-----------------------------------------------------------

第二章 Lpc的数据类型

-----------------------------------------------------------

第一节 序言

Lpc的Object是由零个或更多一些的被一个或一个以上函数 *** 纵控制的变量组成的。在代码中函数排列的顺序是不影响Object的特性,但是影响代码的可读性。当你写的那个Object被第一次调用时,Driver将你写的代码装入内存。当每一个Object被调入内存时,所有的变量是没有值的。create()这个函数被调用来初始化Object值。

create()这个函数在Object装入内存后立即被调用。在你读本文时可能对编程一无所知,你可能不知道什么是函数以及它是怎么调用的;或许你有了一些编程的经验,你可能对一个新创建的Object的函数相互调用过程是怎样开始感到迷惑。在这些困惑得到解决之前,你更有必要了解的是这些函数 *** 纵控制的到底是什么东西。所以你最好先来读读这一章:Lpc的数据类型。可以这么说,几乎90%的错误(包括丢失{}和())是由于错误的使用Lpc的数据类型。我认为真正理解这一章能帮助你更容易的编程。

-----------------------------------------------------------

第二节 让计算机理解你

2.1 计算机语言

众所周知的计算机懂得的语言实际上由“0”和“1”组成的机器码。计算机根本不懂得人类的自然语言,实际上它也不懂得我们使用的高级语言,比如BASIC,C,C++,Pascal等等。这些高级语言能让我们更容易的实现我们的想法。但是这些高级语言最终都要被翻译成“0”和“1”组成的计算机语言。

有两种方法能把高级语言翻译成计算机语言:编译和解释。编译类的在程序写完之后用一个编译器将其翻译成计算机语言。编译在程序执行之前就完成了。解释类的翻译的过程在程序执行时进行。由于解释类的语言程序是边执行边解释,所以一般都要比编译编译执行的慢。

不管是哪种语言,他们最终都要被翻译成0和1。但是变量,那些

你存在内存里面的变量,却不可能只是0和1。

所以你必须有一种你

使用的那种编程语言里面的方法来告诉计算机这些0和1应该被当做

整数还是字符,或者是字符串,或者别的什么东西。这样就必须使

用到数据类型。

2.2 数据类型

一个简单的例子:你现在有了一个变量,你把它叫做‘x’并且

赋予它一个十进制整数值65。在Lpc你可以这样的语句来做这件事:

------

x = 65

------

接着你可以做象下面这样的事:

-----

write(x + "\n")

y = x + 5

-----

第一行把65和字母"a"输出到屏幕上

第二行把70这个值赋于变量y

对计算机来说有个问题:它不知道你所说的 x = 65中的65什么意思.

你认为是65,但是计算机可能认为是:

00000000000000000000000001000001

但是,对计算机来说,字母'A'也是被当做:

00000000000000000000000001000001

所以,当你想让计算机明白 write( x + "\n" ), 它必须有一种方法

知道你想看到的是65而不是'A'.

计算机就是通过数据类型来区分65和'A'. 一种数据类型简单的说就

是在内存的某处, 那里代表了或者说指向某个给定的变量, 这些内存

储存的数据是什么类型的. 每个LPC的变量都必须有它对应的变量类型.

在上面给的例子, 本应在那些代码之前有下面一行:

-----

int x

----

这一行告诉Driver x应该指向什么类型的值, 它应该被当做数据类型'int'

来使用. 'int' 是一个32位的整数. 到这里, 你应该有数据类型的基本

印象, 以及为什么必须有数据类型. 他们可以让Driver知道计算机存在

内存里面的'0'和'1'到底是什么东西.

2.3 Lpc的数据类型

所有的LpMud的Driver都会有以下的数据类型:

void, int, string, object, mixed, int *, string *,

object *, mixed *

大多数的Driver都会有下面这些重要的数据类型:

float, mapping, float *, mapping *

有一些Driver同时还支持下面这些数据类型:

function, struct, class, char

特别的有MudOS支持的数据类型:(以v22pre8为例)

void, int, string, object, float, mapping, function,

class, mixed, int *, string *, object *, float *,

mapping *, function *, class *, mixed *

2.4 一些简单的数据类型

在Lpc入门里面将介绍以下的数据类型:

void, int, float, string, object, 以及mixed. 对于复杂的数据

类型比如: mapping, array, 以及一些不常用的类型比如: class,

function, 我将在Lpc进阶介绍. 这一节我们主要介绍三种数据类型:

int(整型), float(浮点数)和string(字符串).

一个int(整型)是一个整数, 比如1, 42, -18, 0, -10002938这些

都是整型. 在MudOS中一个整型是一个32位的整数, 有符号的整数.

在实际中int得到广泛的使用, 比如开始介绍变量中的wiz_level,

再比如生物的天赋, 年龄等都通常都是int(整型).

一个float(浮点数)是一个实数, 比如2.034, -102.3453, 0.0,

1132741034.33这些都是一个浮点数. 在MudOS中一个浮点数也是一个

32位的实数, 有符号的实数. float通常不常用.

在Object的数值性质中, 我们通常也就使用int和float, 甚至只用

int, 在变量的初始化中int和float自动被赋为0. 但是一般的Driver

比如MudOS不检查数值越界的情况, 还要注意的是这里的int和float

都是有符号的数, 这两点要注意.

string(字符串)是由一或更多的字符组成, 比如"a", "我是飞鸟!",

"42", "飞鸟15岁.", "I am Trill.", 这些都是字符串. 注意的是,

字符串都是被""括起来, 这样第一能区别象int(整型)42和string(字

符串)"42", 第二可以区别变量名(比如 room)和同名的字符串(比如

"room"). string类型的变量在初始化时, 如果没有显式的赋于一个

字符串比如: 空字符串"", 那将是0, 就是一空的指针.

作为最基本的数据类型int, float和string, 是一些复杂的数据

类型的基础. 对这些数据进行运算和 *** 作的 *** 作符, 将在后面介绍,

不过Lpc的 *** 作符和一般的C/C++的 *** 作符一致. 只是有一点, 就+

Lpc支持string和int或者float直接相加, 比如我们上面提到的:

write(x + "\n")

"\n"是一个字符, x是一个整型的变量, 在Lpc解释执行中, 自动将

x代表的数值转化成一个字符串, 然后把两个字符串接在一起.

当你在编程中使用一个变量, 你必须首先让Driver知道这个变量

代表什么样的数据类型. 这个过程叫做变量声明. 这必须在一个函

数或者一个Object的开始部分进行变量声明. 怎么做呢阿? 就是把

数据类型的名字放在你所要用的变量名前面, 举个例子:

-----

void add_x_and_y()

{

int x

int y

x = 2

y = x + x * x

}

----

上面就是一个完整的函数. 函数名就是add_x_and_y(). 在这个函数中

一开始声明落两个变量x, y都是int.

下面介绍MudOS支持的数据类型:

int

一个整数(32位).

float

一个浮点数(32位).

string

无限长的字符串.

object

指向一个对象的指针.

mapping

一个关系型数组.

function

一种特殊的指针, 指向(object, 函数名)这样一个组合.

arrays

数组的声明采用使用 '*' 跟在一种基本的类型.

void

这种类型对变量没有任何用处, 只对函数有用. 它表明这个函数

不返回任何值.

mixed

这是一种特殊的数据类型, 可以指向任何的数据类型. 如果一个

变量被声明成mixed, 那么Driver不会对它做任何检查.

class

自定义的数据类型, 类似C的struct而和C++和class不一样.

一上是MudOS支持的数据类型.

小结:

对一个变量, Driver需要知道存在计算机内存中的'0'和'1'到底

指的什么东西, 这样我们引入落数据类型. 我们学习3种简单的数据

类型, 同时了解了MudOS支持的各种数据类型. 对于各种 *** 作符, 不

同数据类型有各自不同的 *** 作符, 比如你让 "飞鸟"/"trill", 那

Driver一定会返回一个错误的. 大多数数的 *** 作符和C/C++的一样,

只是+ 还支持字符串和数字相加.

-----------------------------------------------------------

-----------------------------------------------------------

第三章 Lpc的函数

-----------------------------------------------------------

第一节 序言

在前面的介绍中,大家应该知道了Lpc的Object包含能处理变量的函数。

当函数被执行时,它的工作就是处理 *** 作变量,还有是调用(call)别的函

数。变量在函数中被改变 *** 作。变量必须有个数据类型使得计算机能明白

它指向的内存中"0"和"1"到底是什么东西。一个Object的性质通常由它的

包含的变量确定,但是它的特性的表现却是依赖于它包含的函数。一个

Object如果不含有任何一个函数那是不可想象的。那么:什么是函数。

-----------------------------------------------------------

第二节 函数

2.1 什么是函数?

和数学的函数一样,你给Lpc的函数一个值,它能返回一个值。有些语

言,比如Pascal,会区分过程和函数。Lpc和C/C++一样,没有过程,但是

明白这种区别还是有用的。Pascal叫做过程的东西,Lpc叫做类型是void

的函数。换句话说,过程就是什么都不返回的函数。Pascal叫做函数的,

必须返回一些东西。在Lpc中,最无聊的,最简单的,但也是正确的函数

是这样的:

-----

void eventDoNothing() {}

-----

这个函数不接收任何输入,不执行指令,也不返回任何值。

每一个Lpc函数都由三部分组成:

1) 函数声明

2) 函数定义

3) 函数调用

和变量一样,函数必须先有个声明。这样可以让Driver知道:

1) 这个函数将返回的是哪种数据类型。

2) 需要的输入是什么,多少。通常把输入叫做参数。

一个函数声明通常是这样的:

类型 函数名(参数1, 参数2, ..., 参数N)

下面是一个函数声明的例子,这个函数叫 DrinkWater,有一个string

类型的参数,返回的是一个int。

-----

int eventDrinkWater(string str)

-----

在上面的声明中, str是输入的参数的变量名,也可以没有。就是说可以

象下面这样声明 eventDrinkWater()

-----

int eventDrinkWater(string)

-----

函数定义就是代码,它描述了这个函数对传人的参数究竟做了些什么。

函数调用就是别的函数在任何地方使用执行了这个函数。一个函数在它

写完后永远不会被调用,那这个函数的存在的唯一意义只能是浪费内存和

硬盘。一个函数写出来的目的是为了被调用。

下面是两个函数相互调用的例子,两个函数是 eventPrintValue() 和

add(),

-----

/* 首先是函数声明,这个通常是在一个Object的开始部分。

*/

void eventPrintValue()

int add(int x, int y)

/* 其次是函数 write_vals() 的函数定义。我们假定这个函数将被调用

* 是为了描述这个Object.

*/

void eventPrintValue()

{

int x

x = add(2, 2)// 我们指定 x 接收调用函数 add() 后返回的值。

write(x + "\n")

}

/* 最后是函数 add() 的函数定义。 */

int add(int x, int y)

{

return (x + y)

}

-----

有一点是指明的,在XO的编程的风格我们要求所有的函数都必须有声

明,这个在我们最开始时候说明过。但是实际上必须有函数声明的函数

是那些被调用在函数定义之前的函数。我们规定必须有函数声明,这个

只是规定,但是它会给编程带来好处。

在这一节我们知道什么是函数,函数是由什么组成。要记住,写一个

函数的根本目的是为用它,调用它。一个函数永远不会被调用,那它就

失去了存在的价值。通常别人使用你写的函数,通常只关心它能对传人

的参数做些什么加工,就是这个函数的功能是什么,返回什么。因此一

个函数有一个好的函数名,能直接描述这个函数的功能是很重要的。我

在第一章中说明了XO规定的对函数的命名机制。采用统一的命名方式有

助于相互合作提高效率。

2.2 Efuns

也许你已经听说过efun这个词了,他们是外部定义的函数,是

externally defined function 的缩写。就是说,他们是由Mud Driver

定义好的。如果参加过Lpc的编程,或者看过Lpc的代码,你可能找到这

样的一些表达式:this_player(), strcmp(), implode(), filter(),

等等,看起来象是一个函数,而你找遍整个Object以及这个Object继承

的所有Object中都没有这些函数,这就表明他们是efun。efun存在价值

是因为他们执行起来要比一般的Object带有的函数速度快的多,为什么

快呢,因为他们是以计算机直接能理解的二进制的形式存在。对于Object

内部定义的函数,我们通常叫他们是lfun(local function)。一个巫师

主要工作也就是编写一些lfun组成的Object。

在上面的例子中的 eventPrintValue() 中调用了两个函数,第一个是

函数 add(), 这个是有你声明和定义的,这个就是lfun。第二次调用,

是调用函数 write() 这个函数通常就是efun。Driver已经替你声明和定

义好了。你所要做只是调用它。

efun被创立是为了

1) 处理一些很常用的,每天都有许多函数会调用的。

2) 处理internet socket的输入输出。

3) 以及一些Lpc很难处理的事,毕竟Lpc是C的很小的子集。

efun是用C写好的,内嵌在Driver里面的。在Mud起来之前,和Driver

一起编译好的,他们执行起来会快的多。但是正和你期望的一样,他们

的调用和你写的函数的调用方法是完全一样的。总的来说,需要关心的

和一般函数一样,它需要传入什么参数,它将会返回什么的东西。

怎样得到一些efun的信息,比如传入参数和返回的类型,通常在一个

Mud里面,你可以在类似这样的 /doc/efun 的目录底下找到,或者直接

用 help <efun名>指令就可以得到帮助。efun及其依赖于你所在的Mud

的Driver,不同的Driver带有的efun区别是很大。

对于XO,使用的是MudOS,一般的efun,只要用 help 指令就能得到

帮助,或者你多看看源码,看看别人是怎样使用的,当然你如果无论如

何也不能明白一个efun,你可以问问大巫师,他们通常会很乐意和你探

讨的。但是有一点是指出,能自己解决的问题最好自己解决。

2.3 自己动手写函数

用Lpc写Object的函数,是为了表现这个Object的特性。这个特性的

函数实际上就是一些代码按顺序排列,排列的顺序决定了这个函数。一

个函数被调用,函数的代码就按照函数定义中代码按顺序执行。在

eventPrintValue()中,下面这个语句:

-----

x = add(2, 2)

-----

必须在 efun: write() 之前调用,如果你想看到正确的结果。

为了返回这个函

�0�2�0�2�0�2 这个问题很奇怪吗?大概是。不过,当我又看到有人发表诸如“Windows/Office是VC编写的”或者“VC是无所不能的”这种高论,我就禁不住这样问自己。�0�2�0�2�0�2 Visual C++究竟是什么?你平常在其中工作的那个标记着“Microsoft Visual C++”的窗口,真的就代表Visual C++吗?�0�2�0�2�0�2 按照我的理解,Visual C++是一个开发工具包,它大概可以分成三个主要的部分:1. Developer Studio,这是一个集成开发环境,我们日常工作的99%都是在它上面完成的,再加上它的标题赫然写着“Microsoft Visual C++”,所以很多人理所当然的认为,那就是Visual C++了。其实不然,虽然Developer Studio提供了一个很好的编辑器和很多Wizard,但实际上它没有任何编译和链接程序的功能,真正完成这些工作的幕后英雄后面会介绍。我们也知道,Developer Studio并不是专门用于VC的,它也同样用于VB,VJ,VID等Visual Studio家族的其他同胞兄弟。所以不要把Developer Studio当成Visual C++, 它充其量只是Visual C++的一个壳子而已。这一点请切记!2. MFC。从理论上来讲,MFC也不是专用于Visual C++,Borland C++,C++Builder和Symantec C++同样可以处理MFC。同时,用Visual C++编写代码也并不意味着一定要用MFC,只要愿意,用Visual C++来编写SDK程序,或者使用STL,ATL,一样没有限制。不过,Visual C++本来就是为MFC打造的,Visual C++中的许多特征和语言扩展也是为MFC而设计的,所以用Visual C++而不用MFC就等于抛弃了Visual C++中很大的一部分功能。但是,Visual C++也不等于MFC。3. Platform SDK。这才是Visual C++和整个Visual Studio的精华和灵魂,虽然我们很少能直接接触到它。大致说来,Platform SDK是以Microsoft C/C++编译器为核心(不是Visual C++,看清楚了),配合MASM,辅以其他一些工具和文档资料。上面说到Developer Studio没有编译程序的功能,那么这项工作是由谁来完成的呢?是CL,是NMAKE,和其他许许多多命令行程序,这些我们看不到的程序才是构成Visual Studio的基石。�0�2�0�2�0�2 为什么我会觉得“Windows是用VC开发的”这种说法很奇怪?因为它太含糊了。用VC,可以编写MFC应用,也可以编写纯SDK程序,不论哪一种方式,都不一定是非VC不可。只要乐意,我完全可以用UltraEdit来写出一个MFC程序,再用CL编译之,没有必要一定动用VC这个大家伙。而且有许多黑客和买不起Visual Studio的人就是这么干的!用SDK编程就更不需要VC了,Down一个Borlan C++ Compiler下来,或者用lcc之类的编译器,同样可以达到目的。再说了,Windows可不是一个单纯的产品。用VC来编写Windows外围程序是完全不成问题的,可是 *** 作系统的核心部分呢?就算可以用VC来编写代码,调试怎么办?VC自身的调试器对一般的应用功能是够强大的,可是对于系统级的调试根本无能为力,因为这个调试器本身就是依赖于 *** 作系统的。只有系统级的调试程序如debug,SoftIce和Wdebug这些工具才能完成如此重大的任务。�0�2�0�2�0�2 从历史上来看,Visual C++ 1.0的出现晚于Windows 3.0,而且那时候的MFC只有一个雏形而已,用来开发 *** 作系统根本是不可能的事情。在Visual C++ 1.0的前面倒是有一个Microsoft C/C++ 7.0,但是它整体水平不如Borland C++ 3.1,在扩展内存管理方面的功能又不如Watcom C++ ,所以一直没有占据很大的市场。它现在已经不作为单独的产品,但仍然作为Platform SDK的主要组成部分而存在于Visual Studio产品中,而且其功能比过去也不可同日而语了。到Windows 95问世的时候,MFC仍然在尽力追赶 *** 作系统的功能。应该说Visual C++ 5.0是一个转折点,一方面MFC已经发展比较完善,另一方面, *** 作系统的基本结构也已经稳定,后面就主要着眼于系统整合与完善作为商务平台的功能。已经稳定的系统不可能再进行翻天覆地的修改,所以,我比较能够接受“Windows系统是用Microsoft C++和MASM作为编译器完成的”这种说法。研究Windows的系统文件可以看出,很多文件显示出来的Linker Version明显是Microsoft C++编译器。至于代码是用什么编写的?我不知道,也不想知道,除了Developer Studio的编辑器之外,任何好的文本编辑器都能够做到这一点。�0�2�0�2�0�2 Visual C++是无所不能的吗?唔,最好也是分开来说。Developer Studio肯定不是—它只是个外壳而已。MFC呢?也不是。一方面它是对API的封装,离开了API它就什么也干不了;另一方面,MFC对API的封装也不够全面,有些时候还是要直接调用API函数才能够“为所欲为”。至于Platform SDK,倒真的可以说它几乎是无所不能的。不过,过分强调这一点并没有太大意义。只要有一套完整的编译器,和必须的支持文件,其他开发工具也可以说是“无所不能”的,比如Borland C++ Compiler或者lcc都可。�0�2�0�2�0�2 老实说,我并不喜欢“无所不能”这类字眼。关键在于各人的理解不同。如果我较起真来,说能不能写个VC程序让电脑拿起鼠标砸向我不喜欢的老板,你说它能办得到吗?所谓的“无所不能”究竟有何意义?让我用VC写一个Server,能在普通工作站上支持每秒几千万的访问量,杀了我也办不到,不管VC的优化手段是多么有效。在具体的平台上,在特定的 *** 作系统中,不论多么强大的工具,最终还是要受到平台和系统本身的限制。大家应该知道这个悖论吧:上帝能否制造出一块他自己也举不起来的石头?�0�2�0�2�0�2 我也常常看到“MFC永远不会过时”或者“C++是不会灭亡的”这种发言。我理解发言人的心情,不过这种说法绝不客观。一种语言也好,一个Application Framework也罢,它们之所以有今天的地位,并不是纯粹自然形成的,有许多复杂因素的作用,也有时势造英雄的理由在内,所谓“居高声自远,非是籍秋风”是也。历史的舞台从来不是为某人专设,即使真有所谓万古长青的怪胎,恐怕也正应了那句老话:“众人都死了,只剩咱们两个老妖精,有什么意思!”我们现在使用的语言,不论Basic,Pascal还是C++,甚至如日中天的Java和C#,终究都会有功成身退的一天。这并不是我们的损失,相反,薪尽火传,一种语言中好的,合理的因素,肯定会被后续者所继承和发扬,自然界的新陈代谢本该如此。�0�2�0�2�0�2 天空没有飞翔的痕迹,而飞鸟已经飞过。一种语言只要曾经在历史上留下浓墨重彩的一笔,完成它“为先贤继圣学,为万世开太平”的历史使命(有点夸张),这就够了,何必缠绵不舍作儿女之态!不知道我有生之年会不会看到C++的消亡,如果真有这么一天,我会拍手欢呼,因为这说明已经有了另外一种更新更好的语言来代替它(或许是几种)。不过照我猜想,像C++这种轰动武林惊万教的语言,其灭亡恐怕也不会是悄无声息,而多半属于“始皇既没,余威震于殊俗”那种情况。�0�2�0�2�0�2 最后请允许我发表一点感慨。语言的优劣其实是一个无需讨论的问题,个人的经历和所处环境在很大程度上就决定了你对某种语言的看法,这是很个人的东西。好比碰到一位法国朋友,他多半会自豪的告诉你:法语是世界上最好最优美的语言。对这种说法我会微笑表示赞同,并且欣赏他的民族自豪感,而不会觉得这是对汉语或者英语的贬低—虽然我心底里一直深信,汉语才是世界上最好的语言。当然,如果他对我说“你们那些破烂中文是些什么玩意”,那我可能就是另外一种反应了。说了这么多,意思无非是想少些无谓的争论罢了。常在论坛上看到“XXX是最好的语言(编译器),XX是什么东西”之类发言。我不想和他们争论,一个人对一样东西既然完全失去了接触和了解的兴趣,那么说什么大概都没有用了。只是觉得遗憾而已。人世间的隔膜与误会,大多是由于彼此不了解而引起,而多少悲剧正是由此而发生呵。在编程的世界里大概不会这么严重,不过言为心声,多少也可看出个人的品行。如果某个人A常在我面前说B的坏话,那么我对B不会有恶感,相反我对A的印象分要减去20。自己不了解的人或者事,不管,不说,也就是了。何至于恶言相加呢。我参加工作的时间不长,各种各样的软件工程师倒是见过不少。就我看到的情况,程序员实在是很沉默寡言的一类人,平时总是表现的温文尔雅,有时候却难得的能见到他们大发脾气,扔鼠标,砸键盘,捶显示器,干什么的都有(这种情况多半是遇到没有办法除掉的Bug了)。面对亲人和朋友的时候他们有时候会选择长久的沉默,只有坐到机器前面时才会发现他们的痴迷和狂热。虽然普遍的不善言谈,但是他们似乎总能在游戏或者网络中找到发泄的方式。所以我在各种各样的论坛上看到语法错误不忍卒读的文字,看到互相指责乃至于人身攻击的情况,甚至看到许多不雅的词汇,虽然心情无论如何愉快不起来,但是我想我能理解。只是,我仍然感到担心,毕竟程序员这一行干几年就了不起了,而人生还有很长的路要走呢。没有一个健全的心态,没有足够为人处世的技巧,30岁以后的人生该如何把握呢?


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/12044482.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-20
下一篇 2023-05-20

发表评论

登录后才能评论

评论列表(0条)

保存