[工学]易语言经典教程_第1页
[工学]易语言经典教程_第2页
[工学]易语言经典教程_第3页
[工学]易语言经典教程_第4页
[工学]易语言经典教程_第5页
已阅读5页,还剩55页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、.开始时间:2005年11月10日,13:56:50第一章、基础知识1.1基本数据类型1.2变量和常量1.2.1变量和常量的命名1.2.2变量的作用范围1.2.3静态变量1.2.4变量的初始值1.3运算符1.3.1运算符的优先级1.4流程控制1.4.1如果、如果真、判断1.4.2判断循环、循环判断1.4.3计次循环、变量循环1.4.4到循环尾、跳出循环1.4.5返回、结束1.5子程序(函数)1.5.1子程序参数(参考、可空)1.5.2子程序的递归1.5.3子程序的静态局部变量1.6自定义数据类型1.6.1例黑客帝国屏保1.6.2自定义数据类型的内存存储1.7数组1.7.1数组的维数1.7.2数

2、组的排序1.7.2.1冒泡排序1.7.2.2选择排序1.7.2.3插入排序1.7.2.4快速排序1.7.2.5自定义数据类型数组的多级排序1.7.3例扫雷游戏第二章、字节集将字节集显示为十六进制文件分割机数据隐藏电子贺卡配置信息写入exe文件exe文件捆绑exe文件的自校验Windows API和动态链接库<枚举窗口,枚举进程><使窗口可移动><窗口子类化><动态菜单><动态组件><文件拖放><读取中的资源扑克牌图片><读DOS程序执行结果><自制皮肤><远程线程>。文件系统

3、<模拟资源管理器><shell菜单><文件格式>编写自己的文件格式易语言模块编程注册表文件关联、命令行处理、文件右键菜单枚举注册表注册表模拟器图像处理取图像宽度、高度取像素字节集各种图像运算方法面向对象编程类、封装、属性、行为、继承、多态性矢量图形(面向对象程序设计)CAD系统EMF文件的读写OpenGL三维图形编程DirectX游戏编程网络编程<Web服务器><聊天程序><远程控制><邮箱登录器><IP追捕><天气查询>数据库编程界面编程<使用易容大师进行界面编程>第一章、

4、基础知识1.1基本数据类型计算机程序是用来采集和处理现实世界的数据的,而现实世界的数据又是多样的,比如数量、名称、状态、温度、时间、图像等等,计算机程序要处理这些数据,那么其编程语言也必须规定相应的类型,不同的数据类型用来保存不同类型的数据。易语言中的基本数据类型和其存储的信息如下表所示:表1.1易语言中的数据类型数据类型名能存储的数据初始值字节型数值型,表数范围:0,255,占用1个字节的存储空间。0短整数型数值型,表数范围:-32768,32767,占用2个字节的存储空间。0整数型数值型,表数范围:-2147483648,2147483648,占用4个字节的存储空间。0长整数型数值型,表数

5、范围:-9223372036854775808,9223372036854775807,占用8个字节的存储空间。0小数型数值型,表数范围:,占用4个字节的存储空间。0双精度小数型数值型,表数范围:,占用8个字节的存储空间。0逻辑型表示真假、男女等具有二值性的数据,占用4个字节的存储空间。假日期时间型表示年月日时分秒的数据,占用8个字节的存储空间。*文本型用来表示描述性的文字、符号等的数据。易语言的文本行变量可以大于64KB。“”字节集可用来表示任何数据,比如图片、视频、声音等,其表示的数据的意义取决于数据的设计者。子程序指针表示子程序在内存中的地址,这是专门为编程而设的数据类型。占用4个字节的

6、存储空间。0*日期时间型的初始值是1899年12月30日0时0分0秒在上表中我们主要看一下数值型的数据类型,因为这种类型的数据我们接触到最多的。我们看到,长整数型的数据表示的整数范围最大,而双精度型的数据表示的范围也大,精确度也最高,那为什么还需要有比他们范围小的数据类型呢?呵呵,这正如尺有所短,寸有所长。首先,它们在内存和磁盘中占用的空间不同,对于字节型的数据,只占用1个字节,而长整型的数据则占用8个字节之多,对于要表示人的年龄这样的数据,字节型经济又实惠人的年龄不会是负数,也不可能超过255岁;其次,运算速度不一样。我们知道,当前主流的计算机都是32位的,在内存中数据是4字节对齐的,那么长

7、度为4字节的数据,在运算时和在内存中移动时速度是最快的,如果你的程序不吝惜内存,而更在乎速度的话,就尽量采用长度为4字节的数据类型吧。字节集数据类型是其他的编程语言中所没有的,是易语言的一个很有特色的数据类型,在文件处理、类对象的持久化中有重要用途,以后会有专门章节讲述。1.2变量和常量所谓变量,就是其中保存的数据可以变化的一个数据容器(在易语言的早期版本中,变量都叫容器)。简单地说,变量就是在内存中保存数据的地方,而其中的数据是可以随时修改的。顾名思义,常量就是恒定不变的量,其中的数据不能被修改。如果在程序中有语句修改了常量的值,编译器会报错。在编辑源代码的任何时候,可以通过点选菜单插入&g

8、t;常量来插入常量。在易语言中,要使用某个常量,必须在前面加上“#”号。有同学可能会问:“既然常量的值是不可改变的,那么要常量有什么意义呢?在源代码中直接使用其值不就行了吗?要定义一个常量,还要想心思取个名字,不是多此一举吗?”其实不然,使用常量有很多好处:1.简化代码输入。比如你要写个与数学相关的程序,要大量使用,我们知道=3.1415926535897932384626433832795,每次在使用的时候,输入这么一长串数字是不是很麻烦?容易输错且不说,读代码的时候还不知其含义,所以我们不如定义一个常量“派”,这样写代码和读代码都方便多了,运行时速度也快,最后编译成exe文件占用的存储空间

9、也少。2.使代码更易于阅读。对于键盘上的每一个按键,都有一个数字型的扫描码,比如空格键的代码是32,回车是13,如果不使用常量,你如何记得住它们?幸好易语言编程环境已经给我们定义好了这些常量,我们可以直接使用。随着编码的深入,你会更多地发现常量有时很好的不要怕麻烦,巧妙地使用常量,有时会有意想不到的效果。1.2.1变量和常量的命名在易语言中,对常量名、变量名和函数名的等需要命名的地方有一定的命名规则,这些名称的命名规则为:名称的首字母必须为全半角字母或汉字,其它字符必须为全半角字母、全半角数字或者汉字。虽然名称中允许半角字符“_”存在,但它被保留为系统专用,因此建议不要使用。与其它的编程语言相

10、比,易语言的命名规则有以下特点:·无长度限制,用户可以尽情地使用长名称来进行名称描述;·永远不会与易语言的关键字产生冲突。譬如:现已存在“如果”命令,但用户仍然可以定义一个名称为“如果”的变量,两者之间不会产生任何冲突。虽然如此,最好还是不要以关键字来命名,因为这样的代码难于阅读和理解。这里顺便说一下“关键字”的概念。在计算机语言中,有些词汇和符号是作为语言的某种特殊的用途而使用的,比如类型定义、流程控制、算术逻辑运算符、预处理指令、编译指令等,在其他的编程语言中,这些词汇和符号是不能作其他用的,比如不能作为变量名。这就好比我的名字是曾劲松,那我生的孩子我能给他取名“曾祖父

11、”吗?不能,因为“曾祖父”就是一个关键字,已经有其特定的用途了。再说一遍,在易语言中虽然可以用关键字来命名变量、常量和函数名等,但建议不要这样当然,中国的考试有时候会刁钻古怪,难免以后易语言进入课堂而有老师出此偏门的考试题目嘿嘿,话题扯远了。1.2.2变量的作用范围在易语言中,常量是全局范围的,也就是说,在程序的任何地方都可以使用该常量。而变量的使用就没有那么随意了,根据变量的作用范围不同,变量可以分为全局变量、程序集变量和和局部变量。全局变量的值在程序的任何地方都可以访问和修改,这虽然方便了编程,但实际上在编程的过程中,如果使用了太多的全局变量,程序写复杂了之后,很容易导致思维的混乱,因为你

12、往往不知道你在程序的哪里修改了该变量。最后往往程序虽然通过了编译,运行的结果却常常莫名其妙。所以,记住一条忠告:能够不使用全局变量,就不要使用,全局变量越少越好。要插入全局变量,请按键盘快捷键Ctrl+G,也可以点选菜单插入>全局变量。程序集变量是在当前程序集的范围内都可以访问的变量,它的范围比全局变量要小些。易语言中的程序集,是指一系列相关子程序和变量的有机组合,说简单点,你可以把程序集想像成资源管理器中的文件夹。一般来说,一个窗口对应一个程序集,当然也有独立于窗口的程序集,关键在于你如何组织你的代码。对于一个有窗口的程序集,你可以把程序集变量看成该窗口的“额外”的属性,这个小技巧在编

13、程时很有用。除去范围小些外,程序集变量的使用和全局变量差不多。所以,程序集变量也要谨慎使用。要插入程序集变量,请将光标放置在程序集名的后面,然后按回车键。局部变量表示在当前子程序(也称函数)的范围内可以访问的变量,它的作用范围最小,也是使用得最多的变量。要在当前子程序内加入局部变量,请按Ctrl+L。在本书中,对于变量和子程序参数的命名有一个约定,那就是:全局变量都以“全”字开头,程序集变量都以“集”字开头,函数参数都以“参”字开头,类的成员数据都以“私”字开头。有了这个约定之后,我们一看变量名就知道它是在何处定义的,方便编写和阅读源代码。1.2.3静态变量只有子程序中的变量可以指定为“静态”

14、类型的变量,所以有关静态变量请参看“子程序”一节。1.2.4变量的初始值变量的初始值是值变量在声明后未给其赋值的情况下变量里面存储的内容。具体的初始值请参见表1.1。1.3运算符运算符就是用来进行运算的符号。在计算机中,运算又分为算术运算、逻辑运算、位运算和赋值运算。算术运算用来计算加减乘除求余数等,这些我们在数学中已经学过了。逻辑运算是进行是非判断以及是非组合的运算,其基本运算规则为:非真为假,非假为真;真且真为真,真且假为假,假且假为假;真或真为真,真或假为真,假或假为假。位运算是对数据位进行操作。我们知道,在计算机内部,所有的数据都是以二进制表示的,比如字符“A”,其ASCII码为65,

15、二进制为1000001,二进制中只有两个数学符号:0和1,数据中的每一个1或0,叫做一位,位运算正是针对这些数据位进行操作的。在易语言中,位运算符以函数的形式存在,其基本规则如下:位与(1,1)=1,位与(1,0)=0,位与(0,0)=0,规则为“有0则0”;位或(1,1)=1,位或(1,0)=1,位或(0,0)=0,规则为“有1则1”;位异或(1,1)=0,位异或(1,0)=1,位异或(0,0)=0,位异或(0,1)=1,规则为“同0异1”;位取反(1)=0,位取反(0)=1;左移是将所有的位左移指定的位数,移出位的被丢掉,右边补0,比如左移(10000012,3)=0001000 2,这里

16、3是十进制数,其他的是二进制数。同理,右移是将所有的位右移,移出的位被丢掉,左边补0。赋值运算是指将值赋给一个或多个变量。易语言中所有的运算符如下表所示:表1.2易语言中的运算符运算符类型运算符名中文操作符号英文操作符号位运算符位与位与band位或位或bor位取反位取反bnot位异或位异或bxor左移左移shl右移右移shr算术运算符负-相乘×*相除÷/整除求余数%或Mod相加+相减-逻辑运算符大于>小于<等于=或=不等于<>或!=大于或等于>=小于或等于<=约等于?=且且and或&&或或or或|赋值运算符赋值=1.3.1

17、运算符的优先级考虑这样一个数学算式:8+3*42,它的结果是如何计算的呢?从数学知识中,我们知道要先算的平方,再与相乘最后加上,而不是简单地按从左至右的顺序来计算。这就是运算符的优先级。同样地,在编程环境中,对一个表达式进行求值时,也有个运算符优先级的问题。上表是按运算符的优先级进行排列的,我们可以看到,在易语言中,位运算的优先级最高(其实它们都是函数),接下来依次是算术运算、逻辑运算、赋值运算。同一个运算符类别中不同的运算符也有优先级的差别,例如对于算术运算符来说,取反()运算符是最高的,其次是乘除,再次是整除、求余、加减等(易语言中没有求幂运算符,是函数)。在上表中,相邻的背景色相同的运算

18、符有相同的优先级。比如相乘和相除有相同的优先级,易语言中的位运算全部是函数,所以也有相同的优先级。相同优先级的运算符按从左至右的顺序求值。对于如下的表达式,变量的最终值是什么呢?变量位或(左移(3,2),3)(53×8)求值过程如下:先计算左移(3,2),3=(11)2,左移两位是(1100)2=12,再与位或,即(1100)2与(0011)2位或,结果为(1111)2=15;再算大于号右边,结果为19,显然15>19的结果是假,所以变量的值为假。在易语言中可以通过以下代码查看运行结果:输出调试文本(位或(左移(3,2),3)(53×8)跟数学算式类似,程序中的表达式

19、也可以通过添加括号来改变运算顺序。如果你不清楚究竟是那个运算符的优先级高,那么就加括号吧!这是确保正确的省事办法。1.4流程控制如同现实世界的数据是多样性的,现实世界的条件也是多样性并且在时刻发生变化的。假如我们要设计计算机程序来处理类似这样的事务: 通过判断外界的温度来控制通过电炉丝的电流以保持大致的恒温当温度高于的时候,减小通过的电流以降温;当温度低于的时候,增大通过的电流以升温; 根据考试得分评等级的程序,如果得分高于分为优秀;介于分到分之间的为优良;低于分的为不及格; 一个图像处理程序,要给一张图片加上个随机的彩点;如果计算机始终只能按照指令的顺序一条条地执行,那么很显然,要处理这样的

20、事情就很棘手了。这样就引入了流程控制的概念,流程控制允许计算机根据不同的条件跳过一段代码继续执行后面的代码,或者跳转到任意指定的指令行去继续执行,或者重复地执行指定的程序段。易语言中设计到流程控制的关键字如下表。关键字说明VB或中的类似关键字如果满足条件执行一个分支,不满足则执行另一个分支。ifelseendif如果真满足条件则指令下面的分支。ifendif判断满足条件则执行该分支,其他的分支不再进行条件判断。如果条件都不满足,则执行最后一条分支(缺省分支)。也就是说,始终只执行其中的一个分支。select case numbercase 1:case n.case else判断循环首如果条件

21、满足,则执行循环体。循环体有可能一次也不被执行。whilewend循环判断首先执行循环体,再进行条件判断。循环体至少被执行一次。doloop while计次循环首执行指定次数的循环,循环的当前次数存入指定的变量中。变量循环首根据指定的取值范围进行循环,循环的当前值存入指定的变量中。for i=5 to 8next i到循环尾不执行此次循环后面的语句而直接进行下次循环。continue跳出循环不再执行循环。break返回返回到调用程序,用于子程序中。return结束退出程序。end1.4.1如果、如果真、判断在论坛上,经常看到有朋友问“如果”和“如果真”有什么区别、“如果真”和“判断”有什么区别

22、的问题,这里我作一个简要的解答。“如果”语句带有两个分支,如果条件成立,执行第一个分支,否则执行第二个分支;“如果真”则仅有一个分支,只有条件满足才执行。如果把“如果”语句的第二个分支留空,那么它的效果和“如果真”是一样的,但在易语言的编程环境中会绘制一条空的流程线,不太美观。所以,如果你要根据条件是否成立来执行某些指令,而不管相反的条件,就要用“如果真”,如果正反两个条件都要兼顾,则需要使用“如果”。易语言的示例代码如图1.4.1-a和图1.4.1-b所示:图1.4.1-a“如果”和“如果真”的区别图1.4.1-b“如果”和“如果真”的等效下面说说“判断”和“如果真”的区别。从表面上看,“判

23、断”也是只有条件为真时执行,但要注意,判断是基于多分支的,只要发现其中一条分支的条件满足,后续分支的条件就不再进行判断,当然其中的代码也不会被执行,如果所有的条件都不满足,则执行默认的分支。用多个“如果真”语句可以写出与“判断”语句等效的效果,但程序执行的效率没有“判断”语句高,为何?因为使用“判断”语句只要发现一条分支满足就不再进行后续判断,而“如果真”语句对每个条件都要进行判断。“判断”语句通常用来对类似的多个条件且在同一个时刻下最多只有一个条件满足的事务进行处理,例如根据用户按下了键盘的某个键、选择了工具条上的哪个按钮来执行相应的功能;在消息循环中处理当前的消息等。图1.4.1-c的示例

24、代码演示了在易语言中用“判断”语句来处理用户单击的工具条按钮。图1.4.1-c用“判断”语句来处理用户单击工具条按钮1.4.2判断循环、循环判断顾名思义,判断循环就是先判断条件是否成立,成立则执行循环体,不成立就结束循环;而循环判断是先执行循环体,再判断条件是否成立,成立则再次执行循环体,否则结束循环。这两个语句很简单,就不再敖述。图1.4.2-a和图1.4.2-b分别是使用“判断循环”和“循环判断”的例子。图1.4.2-a使用“判断循环”将一个文本文件的内容读入列表框中图1.4.2-b使用循环判断随机画圆,直至用户按下ESC键1.4.3计次循环、变量循环记得我读小学的时候,老师常对我说“把每

25、个写错了的字抄写遍!”,我想大家都有过类似的经历吧。对于计算机来说,这正是一个“计次循环”的问题。在你明确知道要循环的次数的时候,推荐使用“计次循环”。计次循环的计数器从开始,每循环一次,计数器自动加1,如果需要的话,可以指定一个变量来保存计数器的值。“变量循环”是高级的“计次循环”,使用起来也稍微复杂一些,可以指定计数器的起始值、终止值和每次递增的数量,同样的,你也可以把计数器的值存入变量中使用。图1.4.3-a用“计次循环”求的阶乘图1.4.3-b用“变量循环”计算以内能被整除的数的和从原理上来说,所有的循环都可以使用“判断循环”来实现,但不同的实现方法除代码量不同之外,执行的效率差距也很

26、显著。推荐的原则如下:能够使用“计次循环”和“变量循环”实现的,就不要使用“判断循环”或“循环判断”,因为前者的执行效率要高得多,尤其是对于次数较多的循环。为什么呢?因为使用“判断循环”或“循环判断”,每循环一次都要执行一次条件判断,而进行条件判断的开销往往是比较大的(尤其是进行文本比较和字节集比较),而“计次循环”则是“明确目的,直奔主题”,效率当然就高多啦!1.4.4到循环尾、跳出循环OK,刚才老师罚你把“羸”字写100遍,也就是给你发了个“计次循环”的指令。当你写到第13个字中途的时候,却差点写成了“赢”字,下面的小“贝”都快写完了,此时你决定放弃继续写这个字,因为错字老师是不算数的,搞

27、不好要再发写100遍呢。于是你把笔移到了下一格,开始写下一个“羸”字暂停!你的这个过程就是“到循环尾”。“到循环尾”并不是放弃整个循环,而是仅仅放弃当前循环中尚未完成的步骤,直接进入下一循环;你继续写阿写,当你写到第90个字的时候,老师突然说“好了,今天时间比较晚了,大家都回家吃饭吧!”。“真郁闷,又让我功败垂成!真是行百里者半九十阿。”,当你心理这样想着的时候,却不知已经深入计算机语言的真谛,因为你已经“跳出循环”。“跳出循环”就是不再继续循环了。这句话对吗?错!如果你在一个多层的嵌套循环中,中层循环中的“跳出循环”指令仅仅跳出当前这一层循环,外层循环还得继续执行的阿。1.4.5返回、结束“

28、返回”主要用在子程序中,返回到子程序的调用者,在返回时可以携带一个返回值。“结束”指令用来终止当前进程,退出程序。这两个很简单,不再敖述。1.5子程序(函数)在我们编程的时候,有一些经常使用的功能,比如求一段文本的长度、计算某个数的平方根、弹出一个消息框提示用户等,如果每次实现这些功能都写一大段代码,是不是很烦琐?结构化的程序设计提出了代码功能模块化的思想,这些实现特定功能的代码模块我们称之为“子程序”。子程序有系统内置的,更多的则需要我们自己编写。系统内置的子程序实现的都是一些通用的功能,是我们编写其他子程序的基石,所以要好好掌握。这些内置的子程序涉及文本处理、算术运算、数组操作、拼音处理、

29、文件读写、系统处理等诸多方面。我的建议是把“工作夹”上“支持库”页中的核心支持库中的所有子程序及其在“提示”窗口中的简要说明都仔细看一遍,不需要全部记下来,只要大概知道有哪些东西、能实现什么样的功能就行了,用的时候再仔细查阅。1.5-a易语言中系统内置函数及其简要说明很显然,仅仅使用易语言内置的子程序无法满足我们编写多种应用程序的要求,所以我们常常要编写自己的子程序。在编辑代码的时候,你可以随时按Ctrl+N或单击菜单项插入>子程序来添加子程序。1.5.1子程序参数(参考、可空)“吃的是草,挤出来的是奶”,这是鲁迅先生对“孺子牛”的亲切描述。我们写的子程序就跟鲁迅先生笔下孺子牛差不多,这

30、里,“草”就是子程序的参数,而“奶”就是子程序返回的结果。当然,有的牛奉献精神更佳,不需要吃草也能挤奶,这就相当于不需要参数的子程序;当然,如果是公牛,吃了草也挤不出奶来,那就是无返回值的子程序虽然它不能挤奶出来,那么肯定有别的用途,比如说跟能挤奶的牛待在一起,会使她们心情舒畅,产出优质量多的奶来;还有的牛,可能不光要吃草,还要吃树叶饲料之类的,那就是带有多个参数的子程序了。通常情况下,一个子程序最多有一个返回值,如果要同时返回多个值该怎么办呢?这里介绍几个技巧:使用“参考”类型的参数。当你勾中了参数名后面的那个“参考”选项的时候,参数传递的就不是它的值,而是它在内存中的地址。你在子程序中对该

31、参数的修改,实际上是对相应的内存中的数据的修改,所以在子程序返回的时候,调用程序中的变量已经被修改了。请看下面的代码片断。1.5.1-a使用“参考”参数类型返获得多个返回值:平均值、最小值和最大值使用数组类型的参数。如果返回的参数个数不定,但类型是一致的,那最好用“数组”类型的参数了。不管勾不勾中“参考”,数组类型始终是传址的。使用自定义数据类型。自定义数据类型将在后面介绍。同样地,不管是否勾中“参考”,自定义数据类型也是传址的。子程序参数的“可空”选项允许用户在调用该子程序时该参数位置不输入参数,这样极大地方便了使用该子程序的用户(很多情况下就是你自己啦),但对于编写该类子程序的程序员来说,

32、会稍微麻烦一些,因为你要考虑到用户是否传入了参数,一般来说,你应该使用如图1.4.1-b所示的形式来设定空参数的默认值。图1.5.1-b设置子程序“可空”参数的默认值1.5.2子程序的递归“从前有座山,山上有座庙,庙里有一个老和尚和一个小和尚。有一天,老和尚给小和尚讲故事,他说:从前有座山,山上有座庙,庙里有个老和尚和一个小和尚。有一天,老和尚给小和尚讲故事,他说:“从前有座山,山上有座庙”我倒,这故事还有玩没玩?!也许你觉得这个故事纯粹是瞎胡闹,那么你错了,这里面可蕴含了巧妙的计算机编程思想递归。“递归”并不是子程序的某个特点,而是一种编程思想。也许你问:递归到底有什么用阿?递归用处可大了,

33、在计算机编程不,在生活中都随处可见,也许只是你没有感觉到。当你在和别人下棋的时候,你是如何思索每一个要下的棋步的呢?或许你是这样思考的:如果我这么走,那么对手会怎样怎样,我再如何如何;如果我那么走,又如何如何等等,根据推理的几步棋,从中挑出一个最优的走法。计算机程序与人类对弈,它的算法也是与此类似的,它会遍历棋盘上当前情况下每一个可能的走法,在内存中形成很多个新的棋局,再以这些棋局为基础,站在你的角度遍历你的所有可能的走法,同时再生成很多新的棋局这样循环到指定的深度(通常就是指思考的“步数”)后,挑出棋局最优的那一盘,回到计算的起始点,再举一个例子,伸出你的左手,看到你掌心的血管没有?从一根主

34、血管分成很多子分支,这些子分支下再分子子分支,子子分支下再分这就是计算机的分形图的基础,而分形图的核心算法必须依赖于递归。还有你每天使用的计算机的资源管理器的目录树,也是递归。这些都是看得见的,还有看不见的,比如编译器的表达式解析,也要用到递归。由此可见,递归是无处不在而且功能强大的,所以一定要好好理解。计算机编程中的递归,指的是子程序不断调用它自身,这想起来总觉得有点不可思议,但确实可以做到。当然计算机中的递归不可能象我前面讲的那个故事那样,否则执行起来没玩没了,任何一台电脑都会死机。所以在使用递归的时候,一定要注意设定终止条件,否则会死得很难看。1.5.2-a使用递归计算阶乘图1.5.2-

35、a的子程序用递归来计算指定数的阶乘。我们知道,任何一个大于1的整数的阶乘实际上等于该数乘以比它小1的数的阶乘,而1的阶乘是1。用数学式子表述如下:用易语言程序来表述正是如上的算法。图1.5.2-b则使用递归来遍历指定的目录,列出指定目录下的所有文件(包括子目录下的文件)。虽然我们不知道一个目录下面有多少层子目录,但子目录与目录间有共性:目录下可能有文件,也可能有目录。当我们发现文件的时候,就输出它;发现目录的时候,就用同样的方式来遍历它。具体代码实现的时候,我们是把当前目录下所有的文件输出,所有的子目录存入一个数组中,再逐个遍历。易语言的“寻找文件”子程序会将当前目录“.”和当前目录的父目录“

36、.”也都列出来,所以在递归的时候要注意排除这两个目录,否则始终在当前目录循环,直至死机。这里如果对易语言的“寻找文件”函数不太理解,可以查看编程环境下的即时帮助。图1.5.2-b使用递归来列出指定目录及其子目录下的所有文件1.5.3子程序的静态局部变量前面提到过,子程序的局部变量可以设置为“静态”类型。所谓“静态”,就是指该变量具有“记忆”功能,对该子程序调用后,该局部变量的值不会被销毁,到下次进入该子程序,它仍然保留上次调用该子程序后的值。普通的子程序变量在每次进入子程序内后都要重新分配内存空间,在退出子程序时自动释放所占用的空间;而“静态”类型的子程序变量跟全局变量和程序集变量一样,在程序

37、启动时就分配了内存空间并初始化(只初始化一次,而不是每次进入子程序都初始化),并且在整个程序运行期间都不释放,所以该类型的变量有“记忆”效应。图1.5.3所示的代码使用静态局部变量和递归分解指定自然数的质因数,理解起来可能有些困难,请仔细思考体会。图1.5.3使用静态局部变量和递归来分解质因数1.6自定义数据类型易语言的基本数据类型可以满足我们普通的编程需求,而对于复杂的应用程序,往往需要我们自己定义专用的数据类型,以简化编程。比如说我们要编一个学生信息管理程序,我们知道,一个学生的信息通常包含以下方面:姓名(文本型)、学号(文本型)、性别(逻辑型)、出生日期(日期时间型)、年级和班级(文本型

38、)、专业(文本型)等。而这些不同类型的信息其实是一个整体,如果能把这些不同的数据类型整合成一个新的数据类型,那么编程起来会方便很多。在易语言编程环境中,点选菜单插入>数据类型来加入新的数据类型,先更改数据类型名称,然后按回车键来添加数据成员。申明好新的数据类型之后,就可以象使用普通变量那样来申明变量了,差别就是在赋值和访问时要使用点语法。图1.6定义和使用一个“学生信息”的数据类型1.6.1例黑客帝国屏保如果你对自定义数据类型使用不习惯,那么下面这个小例子或许会让你熟悉自定义数据类型并增强对编程的兴趣。这里我们来做一个很酷的类似黑客帝国中片头的字符下落特效的屏保,让你的电脑更富有个性!请

39、按下面的步骤操作。1> 新建一个易程序。在上面加一个画板,改名为“画板缓冲”,设置画板的背景色为黑色、文本颜色为绿色,选择一个长型的字体,比如“Impact”,字号为四号,注意,还要将其“可视”属性设为“假”,“自动重画”设为“真”。这个画板我们将作为在后台绘制的一个缓冲画板,用户是看不见的。再加一个画板,改名为“画板可视”,其他的保持默认值。2> 往窗体上添加一个时钟控件,将其“时钟周期”改为50,我们需要每秒钟刷新20张画面。3> 点选菜单插入>数据类型,添加一个如图1.6.1-a的数据类型:图1.6.1-a黑客帝国特效的数据类型4>双击“_启动窗口”,如图1

40、.5.1-b添加几个程序集变量。图1.5.1-b黑客帝国特效的数据类型5>在“_启动窗口_创建完毕”中添加图1.6.1-c所示代码。图1.6.1-c“_启动窗口_创建完毕”事件中的代码这段代码中,读者可能会对初始化下落字符数组下面的计次循环中的代码有点费解,这里解释一下。“字符(取随机数(33,126)”这一句用来随机取一个ASCII范围在33到126之间字符。从图1.6.1-d所示的ASCII表可以看到,这些字符包含所有的可见英文字符和标点符号。如果你只想要数字,那么把这句改成“字符(取随机数(48,57)”。图1.6.1-d ASCII表“取整(取随机数(0,_启动窗口.宽度)

41、47;20)×20”这段代码先从0到窗口宽度中随机取出一个值,然后把该值除以20并去掉小数部分,然后再乘以20,就得到了一个能被20整除的随机数。这样设置以后,所有的字符都会在竖直方向形成列,每列个占用20个像素的宽度。“集下落字符们i.y取随机数(,_启动窗口.高度)”,这句用来将字符的纵坐标设置为屏幕上方的某一个随机值,这样字符就会从屏幕外落到屏幕内。“集下落字符们i.速度取随机数(2,30)”,这句很简单啦。用来设置每个字符下落的速度范围。当然它们下落的速度不应该一样,否则的话就像军队阵列,太呆滞了。6>双击窗体上的计时器,添加以下代码。图1.6.1-e黑客帝国的时钟周期

42、事件代码在这段代码中,程序将后台画板清空,重新设定每个字符(这样字符边下落边翻转。如果只需要0到9之间的数字,可以象前面说明的那样改),将每个字符下落一次,把它们绘制到缓冲画板;如果字符落到了屏幕下边,则再次把它移到屏幕的上边。全部绘制到缓冲画板后,再一起复制到前台的可视画板。为什么要使用两个画板呢?因为如果只使用一个画板,清除后立即重绘会导致画面闪动,在慢速的机器上甚至可以看到字符一个个被绘制出来,效果很差。而采用两画板,绘制完毕后一起复制到前台,用户看不到绘制的过程,所以就不会出现闪屏的情况这就是游戏编程中常使用的“双缓冲”技术。OK,运行以下看看效果吧。是不是很酷?你还可以通过调整字符个

43、数、下落速度、以及时钟周期来获得更满意的效果。7>把它变成屏保吧。这个小程序做成屏幕保护最合适不过了,这里我们使用最简单的一种方法来把它变成屏保。首先,屏保在用户移动鼠标、点按鼠标或敲击键盘时要退出,所以我们按以下步骤添加代码:加一个程序集变量“集鼠标上次横坐标”,类型为整数型,用来进行用户移动鼠标距离的判断,如果鼠标移动的距离很小(比如桌面被震动),则不退出屏保。添加鼠标移动事件、单击事件和按键事件的代码,如图1.6.1-f所示。图1.6.1-f添加适合屏保的退出程序的代码其次,需要将程序编译成“黑客帝国特效exe”文件,再在资源管理器中将扩展名改为“scr”,然后把它拷贝到windo

44、ws XP的system32目录下(如果是windows98/ME,则拷贝到system目录下),打开桌面的属性窗口,就可以把屏保设置为我们刚才写的“黑客帝国特效”了(图1.6.1-g)。不过,真正的屏保程序可没这么简单,还需要涉及到预览窗口、参数配置等,后面章节会有专门介绍。图1.6.1-g设置黑客帝国屏保1.6.2自定义数据类型的内存存储自定义数据类型的使用很简单,我们只需用“.”操作符就可以读取或修改它的各个数据成员了。那么我现在要考考你:能不能不使用“.”操作符而直接修改某个自定义数据类型的成员数据呢?答案是肯定的,我们可以通过直接读取变量的内存地址来做到这一点。要通过修改内存来修改数

45、据成员,我们必须要知道自定义数据类型的内存存储方式。也许你会问:既然可以直接通过“.”操作符来修改数据成员,为何还要舍近求远地直接修改内存呢?这样做,一方面可以加深我们对计算机数据存储器的理解,另一方面,也为后面章节中的向函数中传递自定义数据类型做铺垫。如果你对此不感兴趣,可以先跳过这一小节。首先我们考虑一个简单的问题:文本型的数据的存储问题,一个文本型的变量,它里面究竟保存的是什么?是文本数据本身,抑或仅仅是文本在内存中的地址?我们来研究一下图1.6.2-a所示的代码【注意:这段代码中使用的函数“取变量地址()”在“特殊功能支持库”中,所以在编写和运行代码前,需要配置支持库,方法如下:点击菜

46、单工具>支持库配置,在支持库列表中勾中“特殊功能支持库”】。这段代码的前三行先初始化一个文本变量的内容为“”,然后取该变量的地址,再用“指针到文本”函数将地址中的内容读出来。从逻辑上来说,如果文本型变量保存的是文本数据本身,那么这里应该将字符串正确地输出,但实际上这里输出的却是乱码。由此可见,“文本”变量中保存的不是真正的字符串,那么是什么呢?只有一个可能,那就是指向文本数据的内存地址,也就是说,“文本”变量里面实际保存的是一个整数。既然“文本”变量里面保存的是真实文本数据的地址,那么怎样才能把它取出来呢?“到数值”函数显然不行,易语言又没有提供一个“指针到整数”的函数,因此只能采用一个

47、变通的方法:先用“指针到字节集”转换成字节集,再从字节集转换成整数,就得到了真实的文本地址了。代码片断中中间三行做的正是这个工作,因此得到了正确的地址,输出了正确的结果。最后的两行代码就是直接修改内存中的数据了,也就是修改了“文本”变量中的值。图1.6.2-a文本变量的内存存储为了理解更直观,这里画一个表(表1.6.2-a),从表中我们可以看到,文本变量的内存地址是1242512(在你的机器上也许会不一样),存储的内容是9896255(这个值也会不一样),而内存起始地址为9896255的内存块中保存的是文本“”。内存地址.1242512.9896255.内存内容.9896255变量名文本表1.

48、6.2-a文本变量的内存存储既然文本型变量的存储搞清楚了,那么自定义数据类型的内存存储方式也就不是难事。我猜想应该是这样的:对于一个自定义数据类型的数据来说,其各个数据成员的数据是存储在连续的内存单元中的,如果成员数据的数据类型占用的内存空间是固定的(比如整数型、逻辑型、日期时间型等),数据就直接存储在该内存单元中,如果数据的长度不定(如文本型、字节集等),那么该内存单元保存的是指向它们的内存地址,占用4个字节的存储空间,实际的数据则另外再开辟内存空间保存。我们用代码来证实我们的猜想,我们需要先定义一个这样的数据类型(图1.5.2-b):图1.6.2-b用来测试的数据结构在窗体上添加一个按钮,

49、添加以下代码。因为这段代码太长,所以以文本的方式列出,以后不作说明,均如此。.版本2.支持库spec.子程序_按钮自定义类型_被单击.局部变量某学生,学生.局部变量结构地址,整数型.局部变量临时地址,整数型.局部变量临时字节集,字节集.局部变量生日,日期时间型.局部变量男女,逻辑型.局部变量年龄,整数型某学生.姓名“曾唯思”'文本型,保存的实际是文本的地址,固定长度为4个字节某学生.生日1968年8月8日'日期时间型,固定长度为8个字节某学生.性别真'逻辑型,固定长度为4个字节,易语言帮助文档有误某学生.年龄37'字节型,固定长度为1个字节'以下三行代码

50、获取“某教师”变量中保存的结构的起始地址临时地址取变量地址(某学生)临时字节集指针到字节集(临时地址,4)结构地址取字节集数据(临时字节集,#整数型,)'以下三行代码取出第一个成员数据“姓名”的地址临时字节集指针到字节集(结构地址,4)临时地址取字节集数据(临时字节集,#整数型,)输出调试文本(指针到文本(临时地址)临时地址结构地址4'跳过“姓名”成员的4个字节,到“生日”成员临时字节集指针到字节集(临时地址,8)'日期时间型长度固定为8个字节生日取字节集数据(临时字节集,#日期时间型,)输出调试文本(生日)临时地址临时地址8'跳过“生日”成员的8个字节,到“性

51、别”成员临时字节集指针到字节集(临时地址,4)'逻辑型数据应占4个字节男女取字节集数据(临时字节集,#逻辑型,)输出调试文本(男女)临时地址临时地址4'跳过“性别”成员的4个字节,来到“年龄”成员临时字节集指针到字节集(临时地址,1)年龄取字节集数据(临时字节集,#字节型,)输出调试文本(年龄)'既然已经找到了正确的内存地址,再往其中写数据就很简单了。临时字节集指针到字节集(结构地址,4)临时地址取字节集数据(临时字节集,#整数型,)写到内存(“曾睿姝”,临时地址,)'写姓名临时地址结构地址4写到内存(2003年10月1日,临时地址,)'写生日临时地址临

52、时地址8写到内存(假,临时地址,)'写性别临时地址临时地址4写到内存(2,临时地址,)'写年龄'再用结构成员的访问方式输出看看输出调试文本(某学生.姓名)输出调试文本(某学生.生日)输出调试文本(某学生.性别)输出调试文本(某学生.年龄)很幸运的是,代码的运行结果证实了我们的猜想。从代码的分析我们可以看到,自定义数据类型变量实际保存的也是个内存地址,我们只要找到这个基址,然后顺次根据成员变量的长度进行递增就可以得到这些成员变量的地址了,然后就可以直接进行读取和写入。对于数据长度不定的数据类型,比如文本型、字节集、数组等,保存的又是一个地址,我们再通过该地址进行访问。所以

53、我们看到,对文本型的成员变量的处理复杂一些,转了一个弯。为什么要采取这样的一种存储方式呢?试想一下,如果文本型数据也直接保存在结构的地址中,那么在初始化该结构时就要对该成员变量分配足够的内存空间,因为系统并不知道后续的代码会将该成员赋一个多么长的文本。很显然,这会造成内存空间的极大浪费,也会极大影响程序的运行效率。而仅仅保存一个内存地址,在32位的操作系统上,内存地址的长度始终是4个字节,这样就固定了。如果后续代码修改了该成员变量,那么系统只用动态分配另一个内存块,并且把该内存块的起始地址赋值到该成员变量即可。这段代码的运行过程可以用下表来说明:内存地址.1405472(基址)基址+4基址+1

54、2基址+16.9896243.内容.989624319680808真37.曾唯思.变量名某学生.姓名某学生.生日某学生.性别某学生.年龄长度48417表1.6.2-b自定义数据类型的内存存储嗯,研究了半天自定义数据类型的内存存储结构,好象没什么用,是不是?前面已经说过,这是为后面章节中的在dll函数中使用自定义数据类型作的准备工作,权作铺垫。:P1.7数组计算机处理的数据量往往是很大的,而且通常是同一种数据类型,比如上例中整屏的字符,或者图像中的一批像素,或者一批文本等等。易语言中提供的“数组”数据结构允许我们方便地处理这样的数据。简单地说,数组就是一批同种类型的数据的顺序集合,我们可以通过他

55、们的序号来方便地访问其中的每一个元素。要定义一个指定数据类型的数组,只需要在声明它时输入数组中元素的个数就可以了,如图1.6。图1.7数组的定义方法在给数组变量取名的时候,我喜欢在后面加一个“们”或“数组”以表示它是一个数组。如果你不喜欢,你可以采用你自己的方法。一旦定义了数组,我们可以通过方括号和其顺序索引来访问其中的元素,如“整数们4”。注意:数组的索引从1开始,到所定义的元素个数结束。如果你访问的数组索引不在此范围之内,那就要弹出著名的“数组索引越界”运行时错误而导致程序意外终止。所以,在使用数组时一定要非常小心,应该尽可能做多的判断以确保索引值在其范围之内!“如果在定义数组时我不知道数

56、组里面的确切元素个数该怎么办呢?”你也许会这么问,这时你就把它的元素个数定为“0”吧,就向上面的“文本们”变量一样,然后在程序代码中使用“重定义数组”函数来动态设置其长度和维数,就可以象普通数组一样使用了。1.7.1数组的维数前面我们说过,数组是同一类型数据的顺序集合,这种“顺序”,指的是数组数据是存储在连续的内存单元中的。然而我们可以从逻辑上把这些数据分割成“行”和“列”、或“长”、“宽”、“高”等从而形成多维数组。单维数组很容易理解,那么多维数组如何理解呢?请看下面的图示。图1.7.1-a一行队列是一个一维数组,可以按序号访问其中每个士兵图1.7.1-b行军方阵可以表示为二维数组,要访问其中的一个士兵,必须同时指定行和列图1.7.1-c三维数组,必须同时指定三个序号才能访问其中的一个元素至于三维以上的数组,则无法用图示表示出来了,有什么用处,也只有充分发挥你的想像力了。要改变数组的维数,请使用“重定义数组”函数。该函数可以

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论