一天学会Free Pascal 制作者:ax_pokl
前言 目录 第一章一向是很重要的,无论你是否学过Pascal语言都请看一下。 本教程不是为了NOIP的人写的,本教程不会过多涉及算法。那些脑残学很牛逼的牛群们请立即退散。 本教程是为了使那些学了C语言(或其它编程语言)能够瞬间看懂pascal语言程序的人写的。本教程也适合初学者观看。 本教程是本人写的第一个教程,吐槽是可以的但吐得太多会伤身体。 如果有什么问题,欢迎QQ:395838203。 发现任何错别字或程序bug,也请联系:395838203。 目录
目录 封面 第一章 Pascal语言介绍 第二章 编译写好的程序 第三章 编写简单的程序 第四章 子程序 第五章 单元库 第六章 编译指令 第七章 子界和枚举 第八章 数组集合文件记录指针 附录 封面
第一章 Pascal语言介绍 目录 什么是Pascal?好吃么? Pascal语言是什么语言? 什么又是机器语言呢? 机器语言都是相同的吗? X86指令集包括哪些指令? 什么是计算机程序? 汇编语言又是什么傻逼东西? 为什么需要Pascal语言? 如何让计算机读懂Pascal语言? 编译器是如何运作的? 目录
什么是Pascal?好吃么? Pascal的取名是为了纪念十七世纪法国脑残学家Blaise Pascal(不来色·帕斯卡)。所以Pascal并不好吃。 一个好看的Pascal语言程序很可能长得像这样: program hello_world; begin writeln('Hello World!'); end. 第一章
Pascal语言是什么语言? Pascal语言是计算机程序语言(简称程序语言),使用这种语言不仅能让我们编写计算机程序,例如:Windows操作系统、猜数字小游戏、魔兽争霸3,皮卡丘沙滩排球等等,还可以使我们变得更加脑残。 计算机俗称电脑,是一种糟糕物。它通常是一个屏幕,一个鼠标,一个键盘,和一个黑箱子。有时候人们说的计算机仅仅指它的黑箱子。 这个黑箱子过去曾经是为了帮助脑残学家研究脑残学才被发明的,它的效果就是帮那些懒惰的脑残学家在研究脑残学时减少运动量。 事实上八成的计算机并不能够读懂Pascal语言。计算机只能读懂机器语言,对机器语言作出反应。 第一章
什么又是机器语言呢? 机器语言是一种只有计算机才读得懂的语言,也是计算机唯一能够直接读懂的语言。这种语言只有两个字母:0和1。 第一章 人类阅读机器语言时会发生眼残: 第一章
什么又是机器语言呢? 第一章 世界上只有个别脑残学家看得懂机器语言,所以我们才需要其它容易看得懂的计算机程序语言来告诉计算机我们要它做什么。 机器语言所有的“单词”的集合,叫做计算机的指令集。指令集也就是计算机黑箱子能做的事。 比如“01011001010010”是一个机器语言单词,它的意思是告诉计算机在屏幕上画一个白点。而计算机所有能做的事,基本上就只是在屏幕上画白点点了。 第一章
机器语言都是相同的吗? 有些计算机不仅能够在屏幕上画白点,还可以画红点、绿点和蓝点。 不是所有计算机懂得的机器语言都是相同的,它们的能力有高低。 通常的家用计算器和笔记本计算机都懂得X86指令集。这是一个古老的通用的指令集,即使是今天大多数计算机都认得它。 第一章
X86指令集包括哪些指令? 第一章 1.数据传送指令——就是把数据传来传去,比如传给屏幕。 2.算术运算指令——就是做一些0-255内的简单加减法。 3.逻辑运算指令——就是做一些傻逼的逻辑运算。 4.串指令——顾名思义,就是一串一串执行指令。 5.程序转移指令——就是转移去执行其它的指令。 6.伪指令——计算机不认识的假的指令-_-! 7.其它指令——比如空指令,高级指令,等等。 原来计算机只会做这些傻逼事情。。。。。。 虽然计算机只能够做这些事情,计算机已经显得足够伟大了。 第一章
什么是计算机程序? 第一章 计算机程序(Computer Program,简称程序)是语句的集合。语句是单词的集合。 为了编写一个机器语言程序,人类发明了汇编语言。 汇编语言的词语是一些英文单词的缩略语和一些数字。每条缩略语和一些数字的组合与机器语言指令相对应。这样,人们就不用书写0和1的机器语言代码了。写完程序后对照书本把英文单词和数字翻译成0、1的代码就能完成机器语言程序了。 第一章
汇编语言又是什么傻逼东西? 第一章 一个经过反编译的EXE程序 至少不再是乱码了 事实上,即使有了汇编语言(ASM语言),能够使用英文表达自己的意思,计算机也只能做些很傻逼的事。 如果要它在屏幕上输出“你好,妈妈”几个汉字,你也许需要让它发送无数个指令给屏幕让屏幕画很多很多点。于是就诞生了更加高级的语言,比如Pascal语言。 第一章
为什么需要Pascal语言? 第一章 计算机编程语言分三类:机器语言,低级语言,高级语言。 机器语言就是0、1构成的语言。之前我们说的汇编语言就是低级语言,它唯一的好处就是使用了英文作为记注符而不是数字。高级语言就多了,最有名的莫过于C语言了。Pascal语言也算一种,除此之外还有:FORTAN,C++,Java,VB,Delphi,Lisp,prolog,等等。 还有一些脚本语言虽然不是编程语言,但是也是计算机语言如:bat,vbs,HTML,asp。 第一章
为什么需要Pascal语言? 正因为机器语言、低级语言可读性差,所以我们需要形如Pascal语言的高级语言来编写程序。一个Windows操作系统的程序代码重达1G,它的代码位于%SYSTEMROOT%\System32下包含几亿条指令。即使一个人一秒能够写一条指令,它也一辈子都写不完啊! 使用Pascal语言我们可以快速准确方便地书写程序,但是计算机并不能够直接读懂Pascal语言,所以我们需要一样很重要的东西。 第一章
如何让计算机读懂Pascal语言? 编译器(Complier)是一个由某个超级脑残编写出来的机器语言程序,它能够让计算机自动将一个Pascal语言程序转换成机器语言程序。 program hello_world; begin writeln('Hello World!'); end. 编译器 Complier 囧! 第一章
编译器是如何运作的? 将一个高级语言程序转换成机器语言程序的过程叫做编译。将一个高级语言程序转换成机器语言程序的原理叫做编译原理(感觉像是废话)。 例如编译器获得“writeln('Hello World!');”这条语句后就会分析这条语句的语法,然后将得知这条语句其实就是在屏幕上输出一些白点而已,便把需要做的事转换成乱七八糟的东西。 编译原理十分复杂,因为要理解人类的语言并非易事,所以只有一些脑残人士才能写的出编译器来。 有很多人写过编译器,最有名的莫过于Trubo Pascal编译器,它的Inline-Complie技术曾震惊全世界。 第一章
在哪里能够得到编译器? 第一章 当然是网上下载啦~! 由于Trubo Pascal编译器已经过时,这里推荐Free Pascal编译器:http://sourceforge.net/projects/freepascal/files/Win32/2.6.0/ 如果使用的不是Windows32位操作系统,也可以去这里下:http://www.freepascal.org/download.var 不同的编译器理解Pascal语言略有不同,这造就了略有不同的Pascal语言语法(语言是随着时代变迁的嘛)。以后讲到的的Pascal语言语法都是基于Free Pascal编译器的。 第一章
第二章 编译写好的程序 目录 编写你的第一个Pascal程序 如何查看程序运行结果? 程序出错了怎么办? 部分中文计算机用户遇到的问题 常用快捷键(请熟记!) 大致了解你写的第一个程序 编译Pascal程序用到文件的类型 调试 目录
编写你的第一个Pascal程序 第二章 第一步:打开fp.exe (请确定你已经正确安装了free pascal) 然后你会看到形如右边的界面: 第二步:点击File,New 然后在蓝色的背景中 按照左边的图打字。 切记: 不可错一字,不可倒一字, 不可漏一字,不可多一字! 第二章
编写你的第一个Pascal程序 第二章 第三步:按F9开始编译 会弹出一个对话框叫你保存。 输入hello_world后按Ok。 如果正常,此时你会看 到如下界面: 第四步:按Ctrl+F9运行程序 你会发现屏幕闪了一下, 这表示你编译并运行成功了。 第二章
如何查看程序运行结果? 第二章 想知道刚才程序做了什么事,你可以按Alt+F5查看,或者点击Debug,User Screen来查看。 原来傻逼计算机只是在屏幕上输出了一些白点点。 第二章
程序出错了怎么办? 当你编译了程序后如果有错误,会出现以下提示:这时候你按回车,光标会停在错误出现的后一个字符(没准是下一行)上。你必须修改错误的部分直到程序能够正确编译为止。 如图,最常见的错误莫过于分号漏加。 图中的“;” except but “BEGIN” found 意思就是分号漏加了。 第二章
程序出错了怎么办? 在编译的时候出的错叫做编译错误,还有种错误是在运行的时候出错的,叫做运行时错误。比如你让计算机去计算3除以0,语法上是没有错误但是不符合逻辑。 不同的错误有不同的出错代码,具体请参见附录里的“Pascal错误码表”。 出错提示中的(2,1)是出错的行数和列数。 第二章
部分中文计算机用户遇到的问题 第二章 有时候你家的free pascal看上去像这样: 请别紧张,这只是脑残计算机在和你看玩笑。这表现出你家计算机逻辑混乱的另一面。 事实上,出现这种情况是因为你的计算机使用了错误的代码页。 你可以建立一个bat脚本输入以下内容后再运行: chcp 437 >nul fp.exe 第二章
常用快捷键(请熟记!) 第二章 Alt + Enter Alt + X F2 F3 F9 Alt + F9 Ctrl + F9 全屏 退出 保存 打开 编译并连接 编译 编译连接并运行 看结果 单步步过运行 单步步入运行 加断点 加跟踪变量 第二章
大致了解你写的第一个程序 第二章 栗子:hello_world.pas program hello_world; {程序总是以program和一串英文字母开始} {单词之间用空格隔开} begin {程序的语句部分开始} writeln('Hello World!'); {输出一个字符串} end. {程序总是以end和一个英文半角句号结束} {大括号里写的都是注释} {注释会被编译器忽略} (*除此之外,你还可以用 一个半圆左括号加一个星号和 一个星号加一个半圆右括号表示一段注释*) 1+1=3 //程序结束以后加什么内容编译器 //都懒得鸟你,一行内的两根斜杠后面的 //内容也会被认为是注释。 {你甚至还可以用//嵌套的方法}//表示这是注释! {原来一个pascal语言程序就是这么写的。} 栗子:hello_world.pas 第二章
编译Pascal程序用到文件的类型 第二章 .pas 程序源代码 .bak 程序源代码的备份 .o 目标文件(经过编译的源代码) .exe 编好的程序(经过连接的目标文件) .pp 单元库文件 .ppu 经过编译的单元库文件 .inc 源代码的一部分(头文件) 编译 连接 .pas .ppu .o .pp .exe 第二章
调试 第二章 你可以在学习编写简单的程序以后再来看这一节。 调试是一种检查程序运行时错误的重要手段。 当你学会了一些编程方法并能够熟练书写程序以后,编译错误已经不再是你畏惧的东西了,但运行时错误将永远陪伴你。 世界上最讨厌的事不是程序中有错,而是不知程序的错出在哪里。 一个程序编写完以后进行运行。如果运行的结果和期望的不同,那就需要调试修改。 第二章
调试 第二章 按F8进行一句一句执行。 如果错误出线在某个子程序内,可以按F7进入子程序一句一句执行,F7也会跟进其它文件内的子程序。 按CTRL+F7输入变量名称,随时查看变量的值。 所有的调试功能都在菜单中的debug中。 除了F8和F7以外,你还可以使用F4直接执行到某一条语句后停止。 除此之外,使用CTRL+F8添加程序断点以后再用CTRL+F9也是一种可取的调试方法。 第二章
第三章 编写简单的程序 目录 语句和程序段 最简单的程序 更高级的程序 常量和变量 关键字和标识符 数据类型 定义常量和变量 操作符 运算符 优先级 让计算机进行简单的数学运算 赋值 交换两个变量的值 条件 让计算机求解一元二次方程 循环 嵌套 猜数字游戏 空语句 目录
语句和程序段 第三章 一条语句就是一个句子(俗称一句话,-_-!我不知道该怎么解释)。 除了begin和end.外,Pascal的每条语句都是以;结尾的。 一个程序段就是一段程序,程序段又称为复合语句。 程序段是由begin,一些语句和end;构成的。 语句的种类可多了,在以后的学习中你将一一接触不同的语句。 之前我们能遇到的writeln('Hello World!')是条语句,它其实是调用了SYSTEM单元库中的一个叫做writeln();的过程(过程是子程序的一种)。 第三章
最简单的程序 第三章 一个最简单的程序包含一个程序说明和一个主程序段: program program1;{主程序说明} {定义主程序的各种变量和常量} begin {主程序段开始} writeln('我是一个语句');{主程序段内容} end. {主程序段结束} 主程序段也是程序段,只不过它结尾的最后一个end是以英文半角句号而非分号结束的。也就是说,pascal语言程序的最后一条语句一定是end.。 第三章
更高级的程序 如果你认为Pascal语言只不过是只能在屏幕上输出“Hello World!”的傻逼语言,那么你就错了,因为其实它还能输出更多东西,比如:“caonima!”。。。。。。因为,认真你就输了。 program input_output; var s:string; begin readln(s); writeln(s); end. 栗子:input_output.pas 第三章
常量和变量 第三章 虽说魔兽争霸3也只不过是屏幕上的一些白点点,但是要完成魔兽争霸3,我们还需要更多的手段。 上面一段程序和hello_world.pas不同的是,它使用了var s:string定义了一个字符串变量。 一个变量(或常量)可以用来储存信息,例如一串文字(字符串类型),一个数字(整数类型、实数类型)。貌似只有这些功能。 第三章
常量和变量 第三章 使用const关键字来定义常量。 使用var关键字来定义变量。 var s:string;,中的s就是一个变量,string是它的数据类型(字符串类型)。 通过readln(s);语句,我们可以在屏幕里输入一串文字并保存到变量s中,下次就可以直接用writeln(s);输出字符串s了。 别忘了,定义一个变量以后也需要加分号。 第三章
关键字和标识符 第三章 不是随便一串英文字母都可以用来定义变量的。一个变量(或常量)的名称叫做变量(或常量)的标识符。 标识符不能是关键字(又称保留字)。 标识符不能重复定义。你可以假定关键字是已经被定义的标识符。 标识符不能以数字开头,并且必须由英文字母、数字和下划线组成。标识符是不区分大小写的。 关键字就是Pascal语言预定好的有特殊含义的单词,如program,const,var,begin,end等等。 如果你打入了一个单词是白色的,那他就是关键字了,所以别把关键字用作标识符。(常用的关键字见附录中的“常用关键字列表”) 第三章
数据类型 第三章 每个变量或常量都有它自己的数据类型。 数据类型决定了数据是如何存储在内存中的。 除了字符串类型(string)以外,还有很多数据类型: 整数类型(integer) 字符类型(char) 实数类型(real) 布尔类型(boolean) 数组类型(array) 集合类型(set) 枚举类型(()) 子界类型(..) 文件类型(file) 指针类型(^) 记录类型(record) 对象类型(object) 这些类型都有自己的定义和使用方法,将在之后的章节一一介绍。 第三章
常用数据类型(整数类型) 第三章 整数类型 能表示的数字范围 占用内存 Shortint -128~127 1 整数类型 能表示的数字范围 占用内存 Shortint -128~127 1 Integer -32768~32767 2 Longint -2^31~2147483647 4 Int64 -2^63~2^63-1 8 Byte 0~255 1 Word 0~65536 2 Cardinal 0~4294967926 4 能表示的数字范围越大,占用的内存也就越多。 第三章
常用数据类型(实数类型) 第三章 实数类型 能表示的数字范围 占用内存 Real Singel或Double 4或8 实数类型 能表示的数字范围 占用内存 Real Singel或Double 4或8 Single 1.5E-45 .. 3.4E38 4 Double 5.0E-324 .. 1.7E308 8 Exended 1.9E-4951 .. 1.1E4932 10 Comp -2E64+1 .. 2E63-1 8 内存,又称主存,是你计算机中的一个配件,需要花钱购买。内存越大则价格越高。如果内存不够用了,你的程序就会崩溃。 内存使用的单位叫做字节,一个字节相当于八个开关。 第三章
常用数据类型(布尔、字符类型) 第三章 布尔类型 能表示的数字范围 占用内存 Boolean false或true 1 布尔类型 能表示的数字范围 占用内存 Boolean false或true 1 字符类型 能表示的数字范围 占用内存 Char #001~#128 1 具体1到128号数字表示什么字符,请参见附录中的“ASCII码表”。 其它表示方法: Integer &01001000100101001010 Integer $7F23A8D2 Char 'a' 第三章
常用数据类型(字符串类型) 第三章 字符串类型 能表示的数字范围 占用内存 String #0x???????? 8 字符串类型 能表示的数字范围 占用内存 String #0x???????? 8 字符串类型其实是一个引用类型,它的值指向内存中的一个地址。字符串类型是一种特殊的类型。 输出的字符串必须用单引号括起来,否则计算机会把它当成一个变量,例如writeln(abc);和writeln('abc');的含义是完全不同的。 第三章
定义常量和变量 第三章 使用=定义常量。 使用:定义变量 常量的值是不可修改的。 program pi_const; const PI=3.14159265358979323846264338327950288419716939999999999; {π其实是个有理数} var d:integer; begin write(‘请输入直径’); readln(d); writeln('周长',PI*d); end. 使用=定义常量。 使用:定义变量 常量的值是不可修改的。 虽说可以用变量来代替常量,但是常量的使用可以使程序更简洁并。程序中过多的数字(硬编码)的出现不易于程序的修改和维护。 常量先于变量而定义,在定义某些变量时甚至可以直接使用到常量(如数组)。 栗子:pi_const.pas 第三章
操作符 第三章 对变量进行运算、赋值的符号叫做操作符(Operator)。运算符是操作符的一种。 数学运算符 关系运算符 逻辑运算符 数学运算符 关系运算符 逻辑运算符 加法:+ 等于:= 且:and 减法:- 大于:> 或:or 乘法:* 小于:< 否:not 除法:/ 大于等于:>= 异或:xor 整除:div 小于等于:<= 取余数:mod 不等于:<> 第三章
运算符 第三章 运算符 操作数类型 结果类型 算术运算 +,-,* 整型或实型 / 实型 div,mod 整型 关系运算 =,<> 除文件各种数据类型 布尔 <,> 标准,枚举,子界 <=,>= 标准,枚举,子界,集合 in 顺序类型、集合 逻辑运算 not,and,or,xor 集合运算 +,-,* 集合 赋值运算 := 除文件以外 第三章
优先级 第三章 学过小学数学的小朋友都知道,数学运算都是从左往右进行的。乘除法的优先级高于加减法。 () not, ~,@ *, /, div, mod, and, &, as, shl, shr |, !, +, -, or, xor =, <>, <, <=, >, >=, in ,is else then 第三章
让计算机进行简单的数学运算 第三章 program a_plus_b_1; var a:integer; b:integer; begin readln(a); readln(b); writeln(a+b); end. program a_plus_b_2; var a,b:integer; {定义两个变量时, 可以把它们写在一起。} begin readln(a);readln(b); {语句也是如此} writeln(a+b); end. 栗子:a_plus_b_1.pas 栗子:a_plus_b_2.pas 第三章
字符串连接操作符* 第三章 输入: program a_b_s1_s2; 1 var a,b:integer; 2 输出: 3 12 program a_b_s1_s2; var a,b:integer; s1,s2:string; begin readln(a); readln(b); writeln(a+b); readln(s1); readln(s2); writeln(s1+s2); end. {不同的数据类型之间,操作符的效果也是不同的。} 栗子:a_b_s1_s2.pas 第三章
赋值 第三章 变量和常量的区别在于,变量的值是可以改变的。 赋值符号::= 赋值符号也是一种操作符。 program set_i; var i:integer; begin i:=1024; writeln(i); end. 栗子:set_i.pas 第三章
交换两个变量的值(低级) 第三章 利用第三变量c来交换两个变量的值。 这个程序的优点是简单,缺点是需要用到第三个变量。-_-! program a_b_c_easy; var a,b,c: integer; begin readln(a,b); c:=a; a:=b; b:=c; writeln(a,b); end. 栗子:a_b_c_easy.pas 第三章
交换两个变量的值(中级) 第三章 利用数学运算操作符来交换两个变量的值。 第一行a变成了一开始的a+b。 第二行b变成了a。 第三行,由于a是a+b,b是a,相当于a+b-a=b。 这个程序的优点不需要用到第三变量,缺点是有可能会溢出,例如输入a=20000,b=20000。 program a_b_c_normal; var a,b: integer; begin readln(a,b); a:=a+b;{a变成了和} b:=a-b;{a+b-b=a} a:=a-b;{a+b-a=b} writeln(a,b); end. 栗子:a_b_c_normal.pas 第三章
交换两个变量的值(高级) 第三章 利用位运算操作符来交换两个变量的值。 这个程序的优点就是一个字:牛逼。 (xor是对整型的每位进行异或运算,不懂的问老师吧,反正xor平时不需要。使用位运算还可以加快程序执行速度。) program a_b_c_hard; var a,b: integer; begin readln(a,b); a:=a xor b; b:=a xor b; writeln(a,b); end. 栗子:a_b_c_hard.pas 第三章
条件(if) 尽管我们已经能让计算机进行一些简单的数学运算并将结果输出出来,但是要完成魔兽争霸3,我们还需要一种很重要的语句:条件语句。 if a=b then c:=512; a=b可以是任何一个布尔变量,还记得运算符号一节中有说关系运算符“=”的输出结果是布尔类型么? 如果使用多个布尔变量,切记用括号括起来: if (a=b) and (c=d) then e:=128; 下面一条语句将永远不会被执行: if false then writeln('I am stipud.'); 第三章
条件(程序段) 第三章 then之后跟的语句可以是一条语句,也可以是一个程序段: if c=d then begin a:=b;b:=b+b; end; 切记,如果要写两条语句,必须将这两条语句写在程序段的begin和end;之间,否则第二条语句将不在条件语句的判定内(一定会被执行)。 第三章
条件(if-else) 第三章 if-else语句: if a=b then c:=512 else d:=215; 相当于: if not(a=b) then d:=215; 切记,无论else前的语句是一条语句还是一个程序段,else前的那一个分号必须删掉! if a=b then begin c:=d;e:=f;end else g:=h; 第三章
让计算机求解一元二次方程 第三章 program yi_yuan_er_ci; var a,b,c,d:real; begin write('请输入a:');readln(a); write('请输入b:');readln(b); write('请输入c:');readln(c); d:=b*b-4*a*c; if d<0 then writeln('方程无解!') else begin write('第一个解是:'); writeln((-b+sqrt(d))/2/a); write('第二个解是:'); writeln((-b-sqrt(d))/2/a); end; end. 栗子:yi_yuan_er_ci.pas 第三章
循环 例如我们要一下子重复输出1000句话,一种方法是把writeln('我是剑圣');复制1000遍。但是这种办法十分坑爹而且写出来的程序十分庞大。另一种方法就是使用循环语句。 加入你在魔兽争霸中需要显示1000个剑圣,这1000个剑圣分别叫做“剑圣1”,“剑圣2”,“剑圣3”…“剑圣1000”,这时候你就不得不使用循环语句了。 循环语句有三种:while语句,repeat-until语句和for语句。 第三章
循环(while) 第三章 理论上说,使用while语句可以完成其它循环语句所能做到的所有的事。 while语句用起来是这样的: a:=1; while a<=1000 do begin writeln('我是第',a,'句话'); a:=a+1; end; 和if语句一样,当while后的布尔值是true时就执行后面的语句或程序段,只不过执行完一遍以后还会重新判断是否再次执行。 第三章
循环(repeat-until) 第三章 a:=1; repeat writeln('我是第',a,'句话'); a:=a+1; 这段程序和右边的程序效果是一样的。无论如何,repeat-until之间的语句一定会执行一遍,因为判断是在until执行的。当until后的布尔值为true时就跳出循环。 a:=1; while a<=1000 do begin writeln('我是第',a,'句话'); a:=a+1; end; 和repeat-until不同的是,repeat-until之间可以是多条语句,而while后必须是一条语句或者一个程序段。 until前可不加分号。 第三章
循环(for) 第三章 for a:=1 to 1000 do writeln('我是第',a,'句话'); 这段程序的效果和右边的是一样的。 for语句总是很简洁,但是并非所有循环都能用for语句表示出来。 for语句的循环体变量(这里是a)的值是不可改变的。 a:=1; while a<=1000 do begin writeln('我是第',a,'句话'); a:=a+1; end; 总能把for语句用while语句表达出来。 学会把for用while写出是很重要的,因为有时候你没法把循环用for表达出来。 第三章
循环(for-downto) 第三章 可惜的是,for语句不支持C语言中的步长功能,每次循环循环体变量的值只会加一。 for b:=1000 downto 1 do writeln('我是第',b,'句话'); program goto_10086; label 10086;{定义一个标记} begin b:=1000; 10086:{在这里标记10086} writeln('我是第',b,'句话'); b:=b-1; if b>=1 then goto 10086; {跳转到标记10086} end. 使用goto语句来达到目的 第三章
循环(goto) 使用goto语句虽然有时候能使书写程序变得简便,但是通常情况下它会破坏程序结构。它会让程序不止一个出口,所以应该避免使用。 理论上所有的循环都可以用while语句来完成。 同样道理,break和continue这些会破坏循环结构的语句也应该避免使用,它们是万恶之源,因此在此不再介绍此类语句的使用方法。(就不告诉你,就不告诉你,就不~告诉你!) 第三章
嵌套 第三章 举个栗子你就明白了: if a>b then if c>d then begin while e=f do g:=g+1; if (1+1)=3 then writeln('好的'); end; else后面也可加嵌套,即else if…,分号加不加自己多试试就知道了。 第三章
猜数字游戏 好了,现在你已经有能力让计算机在屏幕上持续得画一大堆点了,虽说离写出魔兽争霸3还有很长的一段路要走,但是凭借这点知识写一些小游戏已经不成问题了。 如果你能理解这个程序,并且正确预料这个程序运行后将会发生什么,那么恭喜你,你已经学会编写一个简单的Pascal语言程序了! program cai_shu_zi; var a,b,i,n:integer; begin a:=1;{最大值} b:=100;{最小值} writeln('请输入一个1-100的整数:'); 栗子:cai_shu_zi.pas(上半部分) 第三章
猜数字游戏 第三章 for i:=5 downto 1 do begin write('你还有',i,'次机会:'); readln(n);{输入的数} if ((b-n)-(n-a)>0) then write('数字太小了傻逼!'); a:=n; end{if} else begin write('数字太大了傻逼!'); b:=n; end;{else} end;{for} writeln('你已经没有机会了。'); writeln('你真是个大傻逼!'); readln(); end. 栗子:cai_shu_zi.pas(下半部分) 第三章
空语句 第三章 一个典型的空语句: ; 空语句用在条件语句上: if a=b then ; 空语句用在循环语句上: while true do ;{死循环} 加了空语句不会怀孕: ;;writeln('加了空语句不会怀孕');;;; 第三章
空语句 第三章 program hello_world_void; begin; writeln('Hello World!'); end. {除了程序名外仔细观察这段程序 和之前的hello_world程序有什么不同 这段程序能否通过编译?为什么?} {答案:能。 begin之后的那个分号相当于空语句。} 栗子:hello_world_void.pas 第三章
条件(case)* 虽然本作者一向不推荐使用case语句,但是作为一个优秀的程序员必须对所有可能出现的语句做一个了解,因为你不用不代表别人不会用,到时你看到case语句说这不是pascal语言因为我没教过我就傻眼了: case i of{这里不加分号} 1:writeln('ok'); 2:a:=b; 3..5:if (1+1)=3 then writeln('yes!');{3..5是子界,有关子界的内容请参看后面几章} else:while true do ;{这里的分号可以不加,只不过这里用了空语句} end; 第三章
循环(break)* 我不推荐大家使用case语句是出于两个原因,第一是因为case语句局限性很大,第二是因为begin和end;不是对称的,case语句中没有begin。 在这里顺便也把break的用法说了吧,还是举栗子: a:=1; while true do begin a:=a+1; if a>1000 then break; end; 就是这样,看不懂也别来问我,只能说你学艺不佳。 第三章
第四章 子程序 目录 函数与过程 参数 返回值 局部变量和全局变量 实际参数和形式参数 重载 递归 超前引用(forward) writeln();和readln(); 场宽(:操作符) 使用其它子程序 程序也是子程序 操作符也是子程序 目录
函数与过程 第四章 细心的小朋友已经发现,在求解一元二次方程中,我们使用了sqrt();来求解一个实数的平方。sqrt();就是一个子程序。 Pascal的子程序分为两种,一种叫做函数(Function),如sqrt();,另一种叫做过程(Procedure),如writeln();。函数于过程的区别在于,函数是有返回值的。例如: a:=sqrt(b); 但是这样是错误的: c:=writeln(d);{这是错误的} 因为writeln();只是一个过程,它并没有返回值。 第四章
函数与过程 第四章 子程序可以理解为“一个独立的程序段”。它的结构就和一个程序一样。 子程序名是一个标识符,例如一个过程: procedure procedure1();{过程说明} {定义各种变量和常量} begin {过程程序段开始} writeln('我是一个语句');{过程程序段内容} end; {过程程序段结束} 第四章
参数 参数其实就是变量。 在使用writeln();时,你会键入writeln('大傻逼');,这个“'大傻逼'”就是一个参数,如果没有这个参数,writeln();只会在屏幕上输出一个空行。 参数的出现使得子程序的效果产生各种变化。一个参数可以是一个变量。使用参数是主程序将变量的值传给子程序的一种手段。 第四章
返回值 第四章 只有函数有返回值。 你可以把函数本身看成一个变量,例如: function inc(i:integer):integer; begin inc:=i+1; end; 在定义函数inc的同时,你就同时已经定义了一个叫inc的整数类型变量。当你把参数传递给函数时,就可以使用它返回的值了,例如: b:=inc(a); 第四章
局部变量和全局变量 第四章 program bian_liang; var a,b:integer; procedure procedure1(); var a,c:integer; begin{过程开始} a:=a+1;b:=b+1;c:=c+1; end;{过程结束} begin{主程序开始} procedure1(); a:=a+1;b:=b+1; end.{主程序结束} 局部变量c是在子程序内定义的,所以在主程序段不能使用变量c,否则会报错。 全局变量b在子程序内也能使用。 变量a在子程序外和子程序内都有定义。被定义的是两个不同的变量。 过程段内改变的是局部变量a的值,主程序段改变的是全局变量a的值。 第四章
实际参数和形式参数 第四章 program can_shu; var a,c:integer;b:real; function abc(i:integer):real; var q:integer; begin i:=i+1;a:=a+5; abc:=i;q:=q+2; end; b:=abc(c); end. a,b,c是实际参数,它们的值无论在子程序内还是子程序外都会被改变。q是变量参数,只在子程序内起效果,用完就结束了,其中i是形式参数,调用结束时值会返回给c。 i和c是两个不同的变量,你不能在子程序外使用i。 无论是何种参数,本作者都不建议通过参数来传递返回值。最好的方法还是使用abc:=i;的方式传递。 第四章
重载 第四章 writeln('abc');和writeln();其实是两个不同的子程序。 参数不同的同名子程序不是同一个子程序,同名参数不同的子程序叫做子程序的重载,一个子程序可以有无数个重载: procedure procedure1(a:integer); begin a:=a+1;end; procedure procedure1(a,b:integer); begin a:=a+2;b:=a+3;writeln('我是重载');end; procedure procedure1(s:string); begin writeln(s);writeln('即使类型不同也可以重载');end; 第四章
递归 第四章 递归其实就是子程序调用它自己 调用自己难道不会出现死循环么?看这个: function jie_cheng(canshu:integer):longint; begin if canshu>0 then jie_cheng:=jie_cheng(canshu-1)*canshu else jie_cheng:=1; end; 递归是一种常用算法,它通常可以大大优化程序,提高程序运行效率,节省内存和钞票。 第四章
超前引用(forward) 第四章 有时候子程序需要相互调用,但是pascal子程序必须先定义才能调用,怎么办?看这里: procedure play();forward; procedure menu(); var a:integer; begin readln(a);if a=1 then play();end; procedure play(); begin readln(a);if a=5 then menu();end; 第四章
writeln();和readln(); write();和writeln();的用法我就不说了,自己试。 虽然read();和readln();都是以回车来结束读取的,但是read();读取是按空格为分隔符来的。 反正我只支持使用readln();来读数据,并且每次最好只读一个。 其实更好的方式是使用其它单元库的其它子程序来读数据,例如keypress(),getMouseEvent()等等。 第四章
场宽(:操作符) 第四章 输出的数据不够整齐?场宽能够帮助你! for i:=1 to 100 do write(i:5); 场宽也能够限制输出数据的长度: for i:=10000 to 10100 do write(i:3); 还有一种双场宽用于输出实数: writeln(1111.222222:3:4); 前一个冒号后是整数部分长度,后一个冒号后是小数部分长度。 输出实数时小数部分是四舍五入的。 第四章
使用其它子程序 SYSTEM单元库中除了writeln();和readln();以外,系统中其实还有许多其它可用的子程序,例如ord();,chr();,sqr();,sqrt();,abs();,trunc();,round();等等,详细请见附录中的“常用子程序列表”。 除了SYSTEM单元库外,你还可以使用其它单元库,例如CRT单元库,这个单元库里有delay();,sound();等子程序。有关单元库的内容将在下一章介绍。 第四章
程序也是子程序 第四章 program inc(i:integer):integer; begin inc:=i+1; end; 说对了,我们的程序其实也是个子程序。当我们用外面的命令或别的程序调用它时,也能获得和子程序一样的效果。 当一个子程序没有参数时,我们可以把它的括号省略,例如: function function_abc:integer; 平时我们定义的program program1其实就是个即没有参数又没有返回值的子程序。 第四章
操作符也是子程序 第四章 你以为:=是和+是系统预定的?那你就错了。操作符的本质也是子程序。 operator + (a,b:integer):integer; begin asm {…} end; end; 这是一个加法操作符的定义,它是用pascal内嵌的汇编语言实现的。我们知道汇编语言是脑残使用的语言,所以我在这里就不多讨论了。 操作符也是可以重载的,具体方法就是看上面的格式使用operator关键字,懒得多说了。 第四章
第五章 单元库 使用CRT和GRAPH单元库 SYSTEM单元库 书写一个单元库 .pp文件和.ppu文件、.o文件的关系 目录
使用CRT和GRAPH单元库 第五章 使用uses语句可以使用单元库预编好的子程序。 每个程序只能有一个uses语句。 uses语句必须紧紧跟在program语句之后。 program program1; uses CRT,GRAPH; begin writeln('随便写点内容');clrscr();end. 和SYSTEM单元库一样,CRT、GRAPH单元库的子程序也写在附录的 “常用子程序列表”中。 使用GRAPH单元库时必须先初始化绘图窗口,自己找参考书吧,GRAPH确实单元库是一个强大的单元库。 第五章
SYSTEM单元库 你在书写程序之前,系统已经默认隐藏包含了uses SYSTEM;这条语句,这就是为什么你能使用writeln();子程序的原因。 在SYSTEM单元中不仅包含了很多子程序,还定义了很多类型,例如integer,char等等。 在源码中的systemh.inc中我们还可以发现,SYSTEM单元库包含了mathh.inc和currh.inc等代码,也就是说我们无需使用MATH单元库就可以使用MATH单元库内预定的子程序等内容。 作者建议大家看一下源码system.fpd内的内容, system.fpd的内容已经写在附录中。 第五章
书写一个单元库 第五章 这不难,看个栗子就知道: unit unit1{单元库声明} interface{关键字,不可省略} const const1=10086; var i:integer;{定义各种常量和变量} procedure procedure1();{这里不用写出程序段,只需写出子程序声明,可以写很多个} implementation{另一个关键字,不可省略} procedure procedure1();{再写一次声明} begin writeln('ok');end;{这里写出程序段} end.{和一个程序一样结束一个单元库} 第五章
.pp文件和.ppu文件、.o文件的关系 第五章 我们通常把单元库的代码保存为.pp文件而非.pas文件。与.pas文件不同的是,按Alt+F9将.pas文件被编译后只生成.o文件,而.pp文件被编译后会生成.ppu和.o文件。 如果.pas文件有使用单元库,则会寻找该单元库的.pp文件,如果没有找到.pp则会寻找.ppu和.o。 把所有的.o文件用link.exe(或者make.exe)连接起来就成了可运行的.exe文件了。当然link.exe也可以将.o文件连接成.dll文件。 第五章
第六章 编译指令 .inc文件和{$I} 编译指令{$define}和{$ifdef} 了解Windows API 调用外部子程序 内嵌汇编语言 目录
.inc文件和{$I} 如果你心情好去官网上下Free Pascal的源码,在source文件夹里除了.pas,.pp你还会发现很多.inc文件。 .inc文件是没法通过编译的,因为它只是源代码的一部分。 如果你有一个文件叫abc.inc,在你的程序中有一句{$I abc.inc},这相当于abc.inc的所有内容全部都照抄到这条语句的这个位置了。 当然,如果在abc.inc中已经加入{$I cba.inc},那么在照抄abc.inc之前会先将cba.inc照抄进去。如果找不到所有所需的文件就无法通过编译,编译时会停留在出错的{$I abc.inc}或{$I cba.inc}上。 第六章
.inc文件和{$I} 同一个.pas可以{$I}同一个文件多次,不同的.pas文件或.pp文件也可以{$I}同一个文件。甚至可以互相{$I}。 {$I}是种编译指令,编译指令都是以“{$”开头的,在编译一个程序之前make.exe会先找到所有编译指令并进行处理,然后再编译。在make.exe之后也可加各种参数来达到同样的效果例如-I abc.inc。 第六章
编译指令{$define}和{$ifdef} 刚才说到两个.pas互相{$I}对方,难道不会出现死循环么?有什么东西能先于{$I}阻止它们吗?有,那就是编译指令本身。 编译指令又叫编译开关,因为编译器最终要将pascal语言转成机器语言,而每种机器懂得的机器语言又略有不同,所以我们需要提供更多的信息告诉编译器如何把自己的代码编译成机器语言,例如你用的机器使用何种指令集。 我用{$define haha}定义了一个叫haha的东西,haha只是有被定义和没有定义两种状态。定义了当然是有用处的。 第六章
编译指令{$define}和{$ifdef} 接下来就有{$ifdef haha}uses CRT;{$endif}就是如果定义过了haha那么这个地方就有“uses CRT;”这条语句,否则这个地方就啥都没写,编译器会忽略这段话。 你觉得这很麻烦?写了又不用为什么不直接不写?这就好像为什么要把程序分块写成子程序一样,为了易于修改。想想看如果把源码拷贝到另一台电脑只要改开头的{$define haha}不需要去删除那修改程序不是很方便么? 除此之外你还可以用{$undef}来取消来取消一个定义,用{$else}控制另一段代码是否使用,甚至你还可以嵌套这些编译指令。 第六章
其它编译指令* 第六章 {$APPTYPE GUI} 可在写windows程序的时候去掉DOS黑框框。 在用DOS单元前{$M $4000,0,0}不然Dos单元的部分函数会出问题。 更多编译指令请见附录“Pascal编译指令”。 第六章
了解Windows API 前面说到了SYSTEM系统单元库,实际上系统单元库也是人写出来的(废话)。那么诸如writeln();这种子程序在系统单元库是如何实现的呢?其实它是调用了Windows API。 Windows API是比尔盖茨的同事们为了自己和大家方便写的一系列动态链接库(DLL),它的地址位于%SYSTEMROOT%\System32,包括kern32.dll,user32.dll等。这些DLL通常使用C++语言写成的,经过编译后变成了机器语言。Pascal语言程序可以使用external和name关键字使用这些在操作系统中预先存在的DLL,实现各种功能(比如在屏幕上画白点点等)。 第六章
调用外部子程序 看看附录里的free pascal源码func.inc是如何定义所有Windows API子程序的你就能学会如何使用name和external关键字调用外部子程序了。 第六章
内嵌汇编语言 别嵌了,先去学汇编语言,然后再去学混合语言编程技术。这一块已经不属于Pascal语言的犯愁了。 反正你看到asm和后面一大堆绿色代码就忽略掉吧。 第六章
第七章 子界和枚举 定义数据类型 子界 枚举 (这一章很短,你知道为什么的) 目录
定义数据类型 第七章 使用type关键字,你可以定义新的数据类型,例如: type mytype=integer; 系统中自带定义了很多其它类型如type qword=int64;,它的目的是为了让其它语言的程序员也可以使用这种类型,因为在其它语言中也许int64就叫做qword。 接着你就可以使用var a:mytype;或者var b:qword;了。 第七章
子界 第七章 你以为integer,boolean等数据类型都是系统预定的?那你就对了!-_-! type integer=-32768..32767; -32768..32767是一个子界类型,它是pascal里的一个基本类型。 所有整数类型都是用子界类型来定义的,这不难。 在系统源码systemh.inc中有这么一样东西: const maxLongint=$7FFFFFFF; 接着就可以这么定义longint类型: type longint=(-maxLongint-1)..(maxLongint); 第七章
枚举 第七章 那么boolean又是如何定义的呢? 把所有可能的值列举出来也是一种办法。 type boolean=(false,true); 。 你也可以直接定义一个变量: var c:(false,true); var d:-10..10; 枚举类型本无操作符,之所以(true and false)会得到false是因为系统预先定义了and操作符。 第七章
第八章 数组集合文件记录指针 目录 数组类型 字符串是一种数组 字符串和字符数组的区别 二维数组 数组常量 集合类型 in(属于)运算符 文件类型 文本文件类型 对文件进行操作 标准文件类型子程序 文件指针 记录类型 记录类型嵌套 使用记录类型 指针类型 指针数组(线性列表) 类型的嵌套 目录
数组类型 要一下子输入100个数据再把它们全部输出怎么办?定义100个变量?你可以使用数组一下子定义100个变量,这100个变量是一个编组,拥有同一个变量名称。 使用array和of关键字定义数组变量。使用方括号使用它。 program shu_zu; var a:array[1..100]of integer;{方括号里的东西叫做下标} i:integer;{下标必须是子界类型} begin for i:=1 to 100 do readln(a[i]); for i:=1 to 100 do writeln(a[i]); end. 第八章
字符串是一种数组 第八章 系统是这么定义字符串类型的: type string:array[0..255]of char; 然后你就会使用它: var str1:string; 如果你觉得字符串不够长,你也可以自己定义: var str2:string[65536]; 有关字符串的子程序,请参见附录“PASCAL字符串操作教程”。 第八章
字符串和字符数组的区别 第八章 array of char string 不能直接读入、输出 可以直接读入、输出 不能进行整体赋值 可以进行整体赋值 位数可以定义很大 位数最多255位 没有标准函数、过程 有标准函数、过程 第八章
二维数组 如果你要建立一个表格,每个格子里存储一些数据,你可以定义二维数组: var a:array[1..10,1..10]of integer; 然后这样使用它: i:=a[2,3]; 你还可以定义更高维数的数组,具体方法就不多说了。 第八章
数组常量 第八章 毛主席曾经说过,常量是一种很重要的东西。 数组常量可以在定义的时候赋值: const a:array[1..3]of integer=(1,2,3); const b:array[1..2,1..2]of char=((1,2),(1,2)); 你理解的,常量的值是不可改变的。 事实上我不建议使用常量数组那是因为我觉得数组的信息比较多写在程序里再改比较麻烦,正确的方法是把大量数据存储在文件中后再提取。有关文件的内容将在这章介绍。 第八章
集合类型 第八章 不用多说,正常情况下大家编程用不着集合类型。如果你念过高级的脑残学大概会知道集合是何种玩应。 type set10=set of 1..10; var a:set10; var b:set of char; (注意,不能定义set of integer,integer有65536种,貌似256种以上的都不能定义) 第八章
in(属于)运算符 第八章 除了>,<,=,+,-,*等运算符以外,使用in运算符可以判断一个元素是否属于一个集合。 那么,如何添加一个元素到集合里呢? var a:set of char; begin a:=[1,2,3]; a:=a+[4]; end; 你也可以自己把它写成子程序后调用。 第八章
文件类型 第八章 如果你家内存只有64K而硬盘有10T,那么把数据全部存在文件里而不是直接在内存中算可以节省大量钞票。 除此之外,你也不想你的程序只能在内存里玩玩吧?如果能操作文件,你就可以获得更多信息并存储更多信息,毕竟绝大多数电脑都是有硬盘的。 不多说,先看看如何定义文件的: var f:file of word;{还记得word是如何定义的?} 第八章
文本文件类型 第八章 SYSTEM单元库中预定了text类型: type text=file of char; 我们可以定义一个文本文件变量: var t:text; 使用一个文件时,我们必须先给定一个文件的文件名: assign(t,'c:\86.txt');{这样文件变量t和c:\86.txt就挂钩了} 然后必须打开文件: reset(f);{读文件} 或者这样打开: rewrite(f);{写文件} 第八章
文本文件类型 第八章 然后就可以操作文件了,别忘了操作完关闭文件: close(f); 文件类型其实是一种记录类型(等会马上将),它存储了文件名,文件句柄,文件指针以及文件状态等信息。 assign();的作用其实是调用Windows API创建文件句柄并将其值放到内存里。 本人强烈建议打开文件后的操作要尽量少,操作完立刻关闭文件,操作时尽量不要使用readln();等待外部读入数据,因为文件一旦打开在关闭在之前不能被其它程序使用。如果想再次操作文件,关闭可以再次打开它,我表示真心不麻烦。 第八章
对文件进行操作 第八章 file of char和file of word有什么区别呢?区别在于操作文件的方式和效果不同。 怎么操作呢? writeln(f,'one'); readln(f,ch); 平时我们用writeln不加f,是因为系统已经默认输出内容到屏幕。(屏幕也是一种文件句柄,一般句柄号是26?) 你用了file of char那么writeln();或readln();的变量就用char,integer就用integer,你理解就行。 第八章
标准文件类型子程序 SYSTEM单元库中,除了assign();,reset();,rewrite();,close();,writeln();,readln();等子程序免费使用以外,还可以使用erase();删除文件,rename();重命名文件,以及append();以末尾添加数据的方式打开文件。这些子程序有些是有返回值的,如果操作失败则返回值不是0。 重点介绍一下eof();和elon();,这两个子程序是用来判断文件是否结束以及行是否结束的,返回值为bollean类型。如果文件指针指向#26(文件结束)或者#13#10(行结束)则会返回true。 第八章
文件指针 第八章 用append(f,str1);设置文件路径时,系统会给内存分配一个整型变量用来存储文件读取的位置。 用reset(f);或rewrite(f);打开文件时,位置会被设置0,即文件开头。 用writeln(f);和readln(f);读写文件时,值会增加。值的大小就是已读取的字节数(你理解的-_-!)。 接着你可以用i:=filepos(f);获取这个变量的值,保存这个位置。 writeln();或readln();后,直接用seek(i);可以回到原来那个保存的位置。你也可以直接用比如seek(100);跳过100字节到文件中间读取。不过别取太大,否则程序会崩溃。你可以用filesize();获取文件大小然后再seek();。 第八章
记录类型 第八章 之前我们说过,文件类型其实是一种记录类型,那什么是记录类型呢? 在C语言中,记录类型又叫做结构类型。依照我的理解,记录类型就是一组类型。先看看如何定义一个记录类型吧: var r:record a:integer; b:boolean; c:char; end; 第八章
记录类型 第八章 注意,record后面是没有分号的,也就是说,从var到end;是一条语句。 上面的语句其实是定义了三个变量,分别为r.a,r.b和r.c,它们分别为整数类型,布尔类型和字符类型。 有了记录类型,你就可以定义一些复杂的类型了: type rd=record i:integer;w:word;end; 接着你就可以定义这个类型的变量: var rd1,rd2:rd; 最后使用变量: rd1.i:=123;rd2.w:=45;{使用句号来使用变量} 第八章
记录类型嵌套 第八章 filerec.inc源码中关于filerec的文件核心类型的定义 type FileRec = Packed Record Handle : THandle;{记录类型} Mode : longint; RecSize : SizeInt;{type SizeInt=integer} UserData : array[1..32] of byte; name : array[0..255] of char; End; : 从上面定义中你还可以发现,THandle类型也是记录类型,也就是说记录类型可以嵌套,只要用多个句号就可以了,如a.b.i等。 第八章
使用记录类型 不能writeln();一个记录类型,因为SYSTEM单元库里没有定义。 可以直接给一个记录类型赋值,只要赋值类型相同,如: var a,b:record i:integer;end; a:=b; 当然也可以a.i:=b.i; 第八章
with操作符* 第八章 var i:integer; var rd:record i,j:integer;end; with rd do begin i:=10086;j:=123; end; 看过就结束,不必多纠结,本人还是建议使用句号,结构清晰。 实际上rd.i和i不是同一个变量,如果同时定义了rd.i和i,那么在使用with的时候with里默认用的是rd.i,你理解的作用域问题。当然with也可以嵌套使用。 第八章
指针类型 第八章 指针类型在C语言中又叫做引用类型。 指针类型是一种很烦的类型。 type point=^integer; 我们知道,内存是计算机里面的一块砖头,这块砖头是由无数个俄罗斯方块拼起来的,每个俄罗斯方块都有自己的编号并且可以刻上一些数字。当我们定义一个变量时,系统会自动分配一个俄罗斯方块给你用来存储数据。由于定义变量必须在程序一开头进行,如果你一开始并不知道自己要用多少变量就很麻烦。 能不能在程序运行的时候再分配俄罗斯方块而不是一开始就全部分配好呢? 第八章
指针类型 虽然我觉得写程序基本不需要指针,而线性列表又涉及算法,但是我觉得简单介绍一下这一块内容还是有必要的。省的到时候有人问我“^”和“@”是什么的时候我就哭了。 指针类型用来存储俄罗斯方块的号码。 比如我定义一个指针类型变量p和一个整型变量a: var p:^integer;a:integer; 然后赋值: a:=1;p:=@a; 在内存的某个俄罗斯方块(比如10086号俄罗斯方块)中存储着变量a的值“1”,接着使用“@ ”符号就可以获得a的值的号码,即10086,所以p=10086。 第八章
指针类型 用CTRL+F7跟踪变量p,然后用Debug-watch命令和F8单步调试我们可以发现p的值实际可能是0x409010,这就是内存中的位置了。 接着,我们就可以使用“^”操作符获取a的信息了: writeln(p^); p是指针类型无法直接用writeln();输出,但是p^是整型类型所以可以输出,它的值就是p所指向的俄罗斯方块里的内容。 当然,如果有var b:integer;,那么你可以直接b:=p^;。 同样,如果有var q:^integer;,那么也可以q:=p;。 注意,使用q:=p;和q^:=p^;是不同的,一个改变的是q,一个改变的是q指向的俄罗斯方块(比如a)。 第八章
指针类型 第八章 指针变量也有自己的俄罗斯方块号码,它和其它变量不同的只是它存储的数据也是一个俄罗斯方块号码。 在未给p赋值之前直接用p^会爆掉。(因为计算机不知道p指向谁。。一般来说计算机申请使用一个俄罗斯方块时会把它的值设置为0,即使是指针变量也是如此,所以你又时会发现计算机游戏中的“该内存不能为read 0x00000000”就是因为没给p赋值然后直接调用p^,而0号俄罗斯方块是不存在的) 第八章
指针类型 第八章 虽然p:=@a;可以把a的俄罗斯方块号赋给p,但是我们想要使用一个新的俄罗斯方块可以这样: new(p); 同样道理如果不需要用了就释放掉: dispose(p); 不释放也可以,不过这样你的内存会越来越少,到最后不够用就爆掉了。不用定义a就可以用p^是很开心的。 第八章
指针数组(线性列表) 第八章 讲了这么多,那么指针类型到底有什么用呢? 前面说到如果你一开始并不知道自己要用多少变量就很麻烦。 特别是一开始需要定义一个很大的数组,需要很多内存的时候,或者你需要定义好多好多个数组的时候,或者你不知道数组应该弄多大(太大浪费内存,太小怕不够用)的时候,指针变量就来了。 我们知道申请一个俄罗斯方块不难,用new();就行。实际上申请一堆俄罗斯方块也不难。 第八章
指针数组(线性列表) 第八章 还记得记录类型吗?记录类型和指针类型嵌套使用的时候有意想不到的效果: type point:^record d:real;n:point;end; var p:point; 如果一个指针变量指向另一个指针变量指向另一个指针变量。。。。。。那么指针变量要存储下一个指针变量的俄罗斯方块号,要存储多余的数据就不行。如果我们把一个指针变量和一个实型变量捆绑成一组,那么我们每次就能多存一个实型变量的数据了。 new(p);p^.d:=794.624; new(p^.next);p^.next^.d:=543.234; 第八章
指针数组(线性列表) 第八章 别急,慢慢看,一定要理解才能懂得指针到底是神马东西。 一般人都是先new(q);一下然后直接p^.next:=q;也可以,然后可以再new(q);或者q^.next:=q等等。 之前说过指针变量一开始其实不是0,而是NIL,叫做空。值为NIL的指针即没赋值的指针叫做空指针,你也可以直接把NIL赋给一个指针变量。 我自己写了一个单元库用来创建动态数组,详细请见附录“线性列表动态数组单元库”。反正只要能用用就行不必纠结。 第八章
类型的嵌套 第八章 数组类型可以用集合类型、记录类型等作为基类型。 记录类型的某个基类型也可以是数组类型。 记录类型的某个基类型可以是指针类型,这个指针类型甚至可以指向这个记录类型。 总之不管怎么样都可以,你要你会用且用的爽。 var a:array[1...100]of record i:record j:integer;end;end; 第八章
类与对象类型* 懒得介绍了,出门右转Delphi。 对象类型(记录类型的升级版): object end; 第八章
附录 目录 ASCII和EASCII码表 基本符号 常用关键字列表 常用子程序列表 Pascal错误码表 四种参数传递方式 system.fpd func.inc 线性列表动态数组单元库 排序算法 目录
ASCII和EASCII码表 http://www.asciitable.com/ http://www.ascii-code.com/ 参见“附录”文件夹“user.pdf”的64页。 附录
基本符号 ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz 0123456789 +-*/=<>><<=>=()[]{}:=.,:;'..^@$% 附录
常用关键字列表 打进去白色的都是关键字。 参见“附录”文件夹“Pascal关键字.xls”。 参见“附录”文件夹“user.pdf”的128页。 附录
常用子程序列表 http://www.freepascal.org/download.var 自行下载fp,在doc,help文件夹里自己找子程序。 也可以直接下fp的源码,然后在source文件夹里直接看源码(教程都叫过你了,你自己慢慢看吧)。 参见“附录”文件夹“rtl.pdf”。 附录
Pascal错误码表 http://www.hu.freepascal.org/lists/fpc-pascal/2000-September/000087.html 参见“附录”文件夹“Pascal错误码表.txt”。 还有一些其它蛋疼的错误,比如你可以试试Uses System,然后会很蛋疼。 附录
Pascal编译指令 http://www.freepascal.org/docs-html/user/usersu68.html http://www.freepascal.org/docs-html/user/user.html 参见“附录”文件夹“chart.pdf”。 附录
四种参数传递方式 Pascal的参数传递都是传值的。 参见“附录”文件夹“四种参数传递方式.doc”。 附录
Pascal字符串操作教程 参见“附录”文件夹“Pascal字符串操作教程.txt”。 附录
Pascal图形操作教程 参见“附录”文件夹“Pascal图形操作教程.txt”。 附录
Pascal退出语句用法 参见“附录”文件夹“Pascal退出语句用法.txt”。 附录
system.fpd 参见“附录”文件夹“system.fpd”。 附录
func.inc 参见“附录”文件夹“func.inc”。 附录
线性列表动态数组单元库 参见“附录”文件夹“ARRNODE.pp”。 附录
排序算法 目标:将一个数组升序排列或降序排列。 参见“附录”文件夹“排序”文件夹。 附录
后记 随着编程水平的逐渐提高,渐渐地你会发现用Pascal语言写一个魔兽争霸从理论上是不成问题的(在屏幕上画点),然而实际操作时会发现难度很大,因为画点的速度太慢了。 使用Driect X是一个快速画画的方法。Direct X是一堆dll,它提供了直接操作底层硬件(屏幕)的子程序。 当然,用Pascal语言写office系列软件还是很累的,要创建一个比如窗口没有c面向对象编程语言方便。不过,使用Pascal解决数学问题或者研究算法还是很理想的。 Pascal语言也是一种和伪代码最相似的语言。 目录
谢谢! 如果你想在本教程基础上修改,请不要删除或修改第一张幻灯片-_-!。