菜单 English Ukrainian 俄语 主页

面向爱好者和专业人士的免费技术图书馆 免费技术库


讲义、备忘单
免费图书馆 / 目录 / 讲义、备忘单

信息学和信息技术。 备忘单:简而言之,最重要的

讲义、备忘单

目录 / 讲义、备忘单

文章评论 文章评论

目录

  1. 信息学。 信息
  2. 计算机中数字的表示。 算法的形式化概念
  3. 帕斯卡简介
  4. 标准程序和功能
  5. 帕斯卡语言运算符
  6. 辅助算法的概念
  7. Pascal 中的过程和函数
  8. 子程序的前向描述和连接。 指示
  9. 子程序参数
  10. 子程序参数类型
  11. Pascal 中的字符串类型。 字符串类型变量的过程和函数
  12. 参赛作品
  13. 文件。 文件操作
  14. 模块。 模块类型
  15. 参考数据类型。 动态记忆。 动态变量。 使用动态内存
  16. 抽象数据结构
  17. 堆栈
  18. 队列
  19. 树数据结构
  20. 树上的操作
  21. 操作实施示例
  22. 图的概念。 表示图的方法
  23. 各种图形表示
  24. Pascal 中的对象类型。对象的概念、描述和使用
  25. 遗产
  26. 实例化对象
  27. 组件和范围
  28. 方法
  29. 构造函数和析构函数
  30. 析构函数
  31. 虚拟方法
  32. 对象数据字段和形式化方法参数
  33. 封装
  34. 扩展对象
  35. 对象类型兼容性
  36. 关于汇编器
  37. 微处理器软件模型
  38. 用户注册
  39. 通用寄存器
  40. 段寄存器
  41. 状态和控制寄存器
  42. 微处理器系统寄存器
  43. 控制寄存器
  44. 系统地址寄存器
  45. 调试寄存器
  46. 装配程序结构
  47. 汇编语言语法
  48. 分段指令
  49. 机器指令结构
  50. 指定指令操作数的方法
  51. 寻址方法
  52. 数据传输命令
  53. 算术命令
  54. 逻辑命令
  55. 控制传输命令

1. 计算机科学。 信息

表示和处理/信息。 数字系统

信息学致力于在科学、技术和生产的各个领域对对象及其关系结构进行形式化表示。 各种形式化工具用于对对象和现象进行建模,例如逻辑公式、数据结构、编程语言等。

在计算机科学中,信息等基本概念具有多种含义:

1) 外部形式信息的正式呈现;

2) 信息的抽象意义、其内在内容、语义;

3)信息与现实世界的关系。

但是,作为一项规则,信息被理解为它的抽象意义——语义。 如果我们要交换信息,我们需要一致的观点,这样才能不违反解释的正确性。 为此,信息表示的解释用一些数学结构来识别。 在这种情况下,可以通过严格的数学方法进行信息处理。

信息的数学描述之一是将其表示为函数

y = f(x,t)

其中 t 是时间,

x 是某个字段中测量 y 值的点。 根据函数参数 x 和 t,可以对信息进行分类。

如果参数是具有一系列连续值的标量,则以这种方式获得的信息称为连续(或模拟)。 如果给定参数一定的变化步长,则该信息称为离散的。 离散信息被认为是普遍的。

离散信息通常用数字信息来标识,数字信息是字母表示的符号信息的一种特殊情况。 字母表是任何性质的有限符号集。 在计算机科学中,经常会出现一种情况,即一个字母表的字符必须由另一个字母表的字符表示,即必须执行编码操作。

实践表明,允许您对其他字母进行编码的最简单的字母是二进制,由两个字符组成,通常用 0 和 1 表示。使用二进制字母的 n 个字符,您可以对 2n 个字符进行编码,这就足够了对任何字母进行编码。

二进制字母表中一个符号所能表示的值称为信息或比特的最小单位。 8 位序列 - 字节。 包含 256 个不同的 8 位序列的字母表称为字节字母表。

数字系统是一组命名和书写数字的规则。 有位置和非位置数字系统。

如果数字的数字值取决于数字在数字中的位置,则称为位置数字系统。 否则,它被称为非位置。 数字的值由这些数字在数字中的位置决定。

2. 计算机中的数字表示。 算法的形式化概念

32 位处理器最多可以使用 232-1 个 RAM,并且可以在 00000000 - FFFFFFFF 范围内写入地址。 但是,在实模式下,处理器使用高达 220-1 的内存运行,地址范围为 00000 - FFFFF。 内存字节可以组合成固定长度和可变长度的字段。 一个字是一个由 2 个字节组成的固定长度字段,一个双字是一个 4 个字节的字段。 字段地址可以是偶数或奇数,偶数地址执行操作更快。

定点数在计算机中表示为整数二进制数,它们的大小可以是 1、2 或 4 个字节。

整数二进制数用二进制补码表示。 正数的补码等于数本身,负数的补码可以通过以下公式得到:

x = 10n - \x\,其中 n 是数字的位深度。

在二进制数字系统中,通过反转位获得附加代码,即用零替换单元,反之亦然,并将最低有效位加一。

尾数的位数决定了数字表示的精度,机器序位数决定了浮点数的表示范围。

算法的形式化概念

只有同时存在某个数学对象时,算法才能存在。 算法的形式化概念与递归函数、正常马尔可夫算法、图灵机的概念有关。

在数学中,如果对于任何一组参数,都有一个定律可以确定函数的唯一值,则该函数称为单值函数。 算法可以充当这样的法则; 在这种情况下,该函数被称为可计算的。

递归函数是可计算函数的子类,定义计算的算法称为伴随递归函数算法。 首先,基本的递归函数是固定的,伴随的算法是平凡的、明确的; 然后引入了三个规则——替换、递归和最小化算子,借助它们在基本函数的基础上得到更复杂的递归函数。

基本功能及其附带的算法可以是:

1) n 个自变量的函数,均等于 XNUMX。 那么,如果函数的符号是​​φn,那么不管参数的数量如何,函数的值都应该设置为零;

2) Ψ ni 形式的 n 个自变量的恒等函数。 那么,如果函数的符号是​​Ψ ni,那么函数的值应该作为第i个参数的值,从左到右计数;

3) 一个独立参数的 λ 函数。 那么,如果函数的符号是​​λ,那么函数的值应该取为参数值后面的值。

3. Pascal语言介绍

语言的基本符号——字母、数字和特殊字符——构成了它的字母表。 Pascal 语言包括以下一组基本符号:

1) 26 个拉丁小写字母和 26 个拉丁大写字母:

2) _(下划线);

3)10位:0 1 2 3 4 5 6 7 8 9;

4) 操作迹象:

+ - O / = <> < > <= >= := @;

5) 分隔符:., ( ) [ ] (..) { } (* *).. : ;

6) 说明符:^#$;

7)服务(保留)字:ABSOLUTE、ASSEMBLER、AND、ARRAY、ASM、BEGIN、CASE、CONST、CONSTRUCTOR、DESTRUCTOR、DIV、DO、DOWNTO、ELSE、END、EXPORT、EXTERNAL、FAR、FILE、FOR、FORWARD、 FUNCTION、GOTO、IF、实现、IN、索引、继承、内联、接口、中断、标签、库、MOD、名称、NIL、NEAR、NOT、OBJECT、OF、OR、PACKED、私有、程序、

程序,公共,记录,重复,常驻,设置,

SHL,SHR,字符串,然后,到,类型,单位,直到,使用,

VAR,虚拟,同时,与,异或。

除了列出的那些之外,基本字符集还包括一个空格。

Pascal 中有一条规则:类型在使用之前的变量或函数的声明中明确指定。 Pascal 类型概念具有以下主要属性:

1)任何数据类型定义了一个常量所属的一组值,一个变量或表达式可以取,或者一个操作或函数可以产生;

2) 常量、变量或表达式给出的值的类型可以通过它们的形式或描述来确定;

3) 每个操作或函数都需要固定类型的参数并产生固定类型的结果。

Pascal 中有标量和结构化数据类型。 标量类型包括标准类型和用户定义类型。 标准类型包括整数、实数、字符、布尔值和地址类型。

整数类型定义常量、变量和函数,其值由给定计算机中允许的整数集实现。

Pascal 具有以下运算符优先级:

1) 括号内的计算;

2)函数值的计算;

3) 一元运算;

4) 操作 */div mod 和;

5) 运算 + - 或异或;

6) 关系运算 = <> < > <= >=。

4. 标准程序和功能

算术函数

1. 函数 Abs(X); 返回参数的绝对值。

2.函数ArcTan(X: Extended):扩展; 返回参数的反正切。

3.函数Exp(X:Real):Real; 返回指数。

4.Frac(X: Real):实数; 返回参数的小数部分。

5.函数Int(X:Real):Real; 返回参数的整数部分。

6. 函数Ln(X:实数):实数;返回实数类型表达式 x 的自然对数 (Ln e = 1)。

7.功能Pi:扩展; 返回值 Pi,定义为 3.1415926535。

8.Function Sin(X: Extended):扩展; 返回参数的正弦值。

9.Function Sqr(X: Extended):扩展; 返回参数的平方。

10.Function Sqrt(X: Extended):扩展; 返回参数的平方根。

值转换过程和函数

1. 程序 Str(X [: Width [: Decimals]]; var S); 将数字 X 转换为字符串表示形式。

2. 函数 Chr(X: Byte): Char;返回 ASCII 表中索引号为 x 的字符。

3.功能高(X); 返回参数范围内的最大值。

4.功能低(X); 返回参数范围内的最小值。

5.函数Ord(X):LongInt; 返回枚举类型表达式的序数值。

6. 函数 Round(X: Extended): LongInt; 将实数值舍入为整数。

7.函数Trunc(X:扩展):LongInt; 将实类型值截断为整数。

8. 过程 Val(S; var V; var Code: Integer); 将数字从字符串值 S 转换为数字表示 V。

序数值过程和函数

1. 过程 Dec(var X [; N: LongInt]); 从变量 X 中减去 XNUMX 或 N。

2. 过程 Inc(var X [; N: LongInt]); 将变量 X 加 XNUMX 或 N。

3. 函数 Odd(X: LongInt): Boolean; 如果 X 是奇数则返回 True,否则返回 False。

4.FunctionPred(X); 返回参数的前一个值。

5 函数 Succ(X); 返回下一个参数值。

5. Pascal 语言运算符

条件运算符

完整条件语句的格式定义如下:

如果 B 则 S1 否则 S2

其中 B 是分支(决策)条件、逻辑表达式或关系; S1, S2 - 一个可执行语句,简单或复合。

执行条件语句时,首先对表达式 B 求值,然后分析其结果:如果 B 为真,则执行语句 S1 - then 的分支,跳过语句 S2; 如果 B 为假,则语句 S2 - 执行 else 分支,并跳过语句 S1。

选择语句

运算符结构如下:

案例 S

c1:指令1;

c2:指令2;

...

cn:指令N;

否则指令

结束;

其中 S 是一个序数类型表达式,其值正在被计算;

c1, c2,..., on - 与表达式 S 进行比较的序数类型常量; instructionsl,...,instructionN - 执行常量与表达式 S 的值匹配的运算符;

指令 - 如果表达式 S 的值不匹配任何常量 c1、o2、on 时执行的运算符。

带参数的循环语句

当 for 语句开始执行时,start 和 end 值被确定一次,并且这些值在 for 语句的整个执行过程中都被保留。 包含在 for 语句主体中的语句对起始值和结束值之间的每个值执行一次。 循环计数器始终初始化为初始值。

带前置条件的循环语句

而B做S;

其中 B 是一个逻辑条件,它的真实性被检查(它是一个终止循环的条件)$;

S - 循环体 - 一个语句。 控制语句重复的表达式必须是布尔类型。 在执行内部语句之前对其进行评估。 只要表达式的计算结果为 Trie,内部语句就会重复执行。 如果表达式从一开始就计算为 False,则不执行包含在前置条件循环语句中的语句。

带有后置条件的循环语句

重复 S 直到 B;

其中 B 是一个逻辑条件,它的真实性被检查(它是一个终止循环的条件);

S - 一个或多个循环体语句。 表达式的结果必须是布尔类型。 包含在 repeat 和 until 关键字之间的语句按顺序执行,直到表达式的结果为 True。 语句序列将至少执行一次,因为每次执行语句序列后都会计算表达式。

6.辅助算法的概念

问题求解算法是通过将整个问题分解为单独的子任务来设计的。 通常,子任务被实现为子例程。

子程序是一些辅助算法,在主算法中重复使用,某些传入量的值不同,称为参数。

编程语言中的子例程是一系列语句,仅在程序中的一个地方定义和编写,但可以从程序中的一个或多个点调用执行。 每个子例程都由唯一的名称标识。

Pascal 中有两种类型的子程序,过程和函数。 过程和函数是声明和语句的命名序列。 使用过程或函数时,程序必须包含过程或函数的文本以及对过程或函数的调用。 描述中指定的参数称为形式参数,调用子程序中指定的参数称为实际参数。 所有形式参数都可以分为以下几类:

1) 参数-变量;

2)常数参数;

3) 参数值;

4)过程参数和函数参数,即过程类型参数;

5) 无类型变量参数。

过程和函数的文本放在过程和函数的描述中。

将过程和函数名称作为参数传递

在许多问题中,特别是在计算数学中,需要将过程和函数的名称作为参数传递。 为此,TURBO PASCAL 引入了一种新的数据类型 - 过程或函数,具体取决于所描述的内容。 (过程和函数类型在类型声明部分中描述。)

函数和过程类型定义为过程的标题和具有形式参数列表但没有名称的函数。 可以定义不带参数的函数或过程类型,例如:

类型

过程 = 过程;

在声明了过程或函数类型之后,它可以用来描述形式参数——过程和函数的名称。 此外,有必要编写那些名称将作为实际参数传递的真实过程或函数。

7. Pascal 中的过程和函数

Pascal 中的过程

该过程的描述由一个标题和一个块组成,除了模块连接部分之外,它与程序块没有区别。 标头由 Procedure 关键字、过程名称和括号中的可选形式参数列表组成:

过程 <名称> [(<形式参数列表>)];

对于每个形式参数,必须定义其类型。 过程描述中的参数组用分号分隔。

该过程的结构几乎与程序完全相似。 但是,程序块中没有模块连接部分。 该块由两部分组成:描述性和执行性。 描述部分包含对过程元素的描述。 而在执行部分,动作用程序可访问的程序元素(例如,全局变量和常量)来表示,这允许获得所需的结果。 过程的指令部分与程序的指令部分的不同之处仅在于 end 关键字以分号而不是句点结束该部分。

过程调用语句用于调用过程。 它由过程的名称和括在括号中的参数列表组成。 运行过程时要执行的语句包含在过程模块的语句部分中。

有时您希望一个过程调用自己。 这种调用方式称为递归。 递归在主要任务可以分为子任务的情况下很有用,每个子任务都根据与主要任务一致的算法实现。

帕斯卡中的函数

函数声明定义了计算和返回值的程序部分。 功能描述由标题和块组成。 标头包含 Function 关键字、函数名称、括号中的可选形式参数列表以及函数的返回类型。 函数头的一般形式如下:

函数 <名称> [(<形式参数列表>)]: <返回类型>;

在 Turbo Pascal 7.0 的 Borland 实现中,函数的返回值不能是复合类型。 而在Borland Delphi 集成开发环境中使用的Object Pascal 语言允许任何类型的返回结果,除了文件类型。

功能块是局部块,在结构上类似于过程块。 函数的主体必须至少包含一个赋值语句,其左侧是函数的名称。 由她决定函数返回的值。 如果有多个这样的指令,那么函数的结果将是最后执行的赋值指令的值。

该函数在调用该函数时被激活。 调用函数时,会指定函数标识符和评估函数所需的任何参数。 函数调用可以作为操作数包含在表达式中。 当表达式被计算时,函数被执行并且操作数的值成为函数返回的值。

功能块的操作符部分指定激活功能时必须执行的语句。 一个模块必须至少包含一个赋值语句,为函数标识符赋值。 该函数的结果是分配的最后一个值。 如果没有这样的赋值语句,或者没有执行,那么函数的返回值是未定义的。

如果在调用模块内的函数时使用函数标识符 - 函数,则该函数将递归执行。

8.子程序的前向描述和连接。 指示

一个程序可能包含多个子程序,即程序的结构可能很复杂。 但是,这些子例程可以处于同一嵌套级别,因此必须先声明子例程,然后再调用它,除非使用了特殊的前向声明。

包含前向指令而不是语句块的过程声明称为前向声明。 在此声明之后的某处,必须通过定义声明来定义过程。 定义声明是使用相同过程标识符但省略形式参数列表并包含语句块的声明。 前向声明和定义声明必须出现在过程和函数声明的同一部分。 在它们之间,可以声明可以引用前向声明过程的其他过程和函数。 因此,相互递归是可能的。

前向描述和定义描述是过程的完整描述。 该过程被认为是使用前向描述来描述的。

如果程序包含相当多的子程序,那么程序将不再是可视化的,将难以在其中导航。 为了避免这种情况,一些例程作为源文件存储在磁盘上,如有必要,它们在编译阶段使用编译指令连接到主程序。

指令是一种特殊的注释,可以放置在程序中的任何地方,普通注释可以放在任何地方。 但是,它们的不同之处在于指令有一个特殊的符号:在右括号之后,没有空格,写入 $ 符号,然后,再次没有空格,指示指令。

示例:

1) {$E+} - 模拟数学协处理器;

2) {$F+}——形成调用过程和函数的far类型;

3) {$N+} - 使用数学协处理器;

4) {$R+} - 检查范围是否超出范围。

一些编译开关可能包含一个参数,例如:

{$I 文件名} - 在已编译程序的文本中包含命名文件

9.子程序参数

过程或函数的描述指定了形式参数的列表。 在形式参数列表中声明的每个参数对于所描述的过程或函数都是本地的,并且可以在与该过程或函数相关联的模块中通过其标识符来引用。

参数分为三种类型:值、变量和无类型变量。 它们的特点如下:

1. 前面没有关键字的一组参数是一个值参数列表。

2. 前面有const关键字,后面跟一个类型的一组参数是一个常量参数列表。

3. 前面有var关键字,后面跟一个类型的一组参数是一个可变参数列表。

值参数

形式值参数被视为过程或函数的局部变量,不同之处在于它在调用过程或函数时从相应的实际参数中获取其初始值。 形参的变化不会影响实参的值。 value参数对应的实际值必须是表达式,其值不能是文件类型或任何包含文件类型的结构类型。

实际参数的类型必须与形式值参数的类型兼容。 如果参数是字符串类型,那么形参的大小属性将为 255。

常量参数

在子程序的主体中,不能更改常量参数的值。 Parameters-constants 可用于排列那些在子程序中不希望发生更改且应禁止更改的参数。

可变参数

当必须将值从子例程传递到调用块时,使用可变参数。 在这种情况下,当调用子程序时,形参被可变实参替换,形参的任何变化都会反映在实参中。

程序变量

在定义了过程类型之后,就可以描述这种类型的变量了。 这样的变量称为过程变量。 与可以分配整数类型值的整数变量一样,可以为过程变量分配过程类型值。 当然,这样的值可以是另一个过程变量,但它也可以是过程或函数标识符。 在这种情况下,过程或函数的声明可以看作是对一种特殊类型的常量的描述,其值为过程或函数。

与任何其他赋值一样,左侧和右侧变量的值必须是赋值兼容的。 过程类型,为了赋值兼容,必须有相同数量的参数,并且对应位置的参数必须是相同的类型。 过程类型声明中的参数名称无效。

此外,为了确保赋值兼容性,如果要将过程或函数赋值给过程变量,则不能是标准的或嵌套的。

10. 子程序参数类型

值参数

形式值参数被视为局部变量;当调用过程或函数时,它从相应的实际参数中获取其初始值。 形参的变化不会影响实参的值。 value参数对应的实际值必须是表达式,其值不能是文件类型。

常量参数

形式常量参数在调用过程或函数时获取其值。 不允许对形式常量参数赋值。 形式常量参数不能作为实际参数传递给另一个过程或函数。

可变参数

当必须将值从过程或函数传递给调用程序时,使用可变参数。 激活时,形参变量被实际变量替换,形参变量的变化反映在实参中。

无类型参数

当形参是无类型变量参数时,对应的实参可以是变量引用,也可以是常量引用。 使用 var 关键字声明的无类型参数可以修改,而使用 const 关键字声明的无类型参数是只读的。

程序变量

在定义了过程类型之后,就可以描述这种类型的变量了。 这样的变量称为过程变量。 可以为过程变量分配过程类型的值。

分配的过程或功能必须是:

1) 不标准;

2) 不嵌套;

3) 不是 inline 类型的过程;

4) 不被中断程序。

程序类型参数

由于过程类型可以在任何上下文中使用,因此可以描述将过程和函数作为参数的过程或函数。 当您需要对多个过程或函数执行常见操作时,过程类型参数特别有用。

如果要将过程或函数作为参数传递,则它必须遵循与赋值相同的类型兼容性规则。 也就是说,这样的过程或函数必须用 far 指令编译,它们不能是内置函数,不能嵌套,不能用 inline 或 interrupt 属性描述。

11. Pascal 中的字符串类型。 字符串类型变量的过程和函数

一定长度的字符序列称为字符串。 字符串类型的变量通过指定变量名称、保留字字符串以及可选但不一定在方括号中指定最大大小(即字符串的长度)来定义。 如果您没有设置最大字符串大小,那么默认情况下它将是 255,即字符串将由 255 个字符组成。

字符串的每个元素都可以通过其编号来引用。 但是,字符串是作为一个整体输入和输出的,而不是像数组那样逐个元素地输入和输出。 输入的字符数不得超过最大字符串大小中指定的字符数,因此如果发生这种超出,则“多余”字符将被忽略。

字符串类型变量的过程和函数

1. Function Copy(S: String; Index, Count: Integer): String;

返回字符串的子字符串。 S 是字符串类型的表达式。 Index 和 Count 是整数类型的表达式。 该函数返回一个字符串,其中包含从索引位置开始的 Count 个字符。 如果 Index 大于 S 的长度,则该函数返回一个空字符串。

2.过程Delete(var S: String; Index, Count: Integer);

从字符串 S 中删除长度为 Count 的字符的子字符串,从位置 Index 开始。 S 是 String 类型的变量。 Index 和 Count 是整数类型的表达式。 如果 Index 大于 S 的长度,则不删除任何字符。

3. 过程插入(来源:字符串;var S:字符串;索引:整数); 从指定位置开始将子字符串连接成字符串。 Source 是 String 类型的表达式。 S 是任意长度的 String 类型的变量。 索引是整数类型的表达式。 Insert 将 Source 插入 S,从位置 S 开始。

4.函数长度(S:字符串):整数;

返回字符串 S 中实际使用的字符数。请注意,当使用以 null 结尾的字符串时,字符数不一定等于字节数。

5. 函数Pos(Substr: String; S: String): Integer; 在字符串中搜索子字符串。 Pos 在 S 中寻找 Substr

并返回一个整数值,它是 S 中 Substr 的第一个字符的索引。如果未找到 Substr,则 Pos 返回零。

12. 录音

记录是属于不同类型的有限数量的逻辑相关组件的集合。 记录的组成部分称为字段,每个字段都由一个名称标识。 记录字段包含字段的名称,后跟冒号表示字段的类型。 记录字段可以是 Pascal 中允许的任何类型,文件类型除外。

Pascal 语言中的记录描述是使用服务词 RECORD 进行的,然后是对记录组件的描述。 条目的描述以服务字 END 结束。

例如,笔记本包含姓氏、首字母和电话号码,因此可以方便地将笔记本中的单独一行表示为以下条目:

键入行 = 记录

FIO:字符串[20];

电话:字符串[7];

结束;

var str:行;

记录描述也可以不使用类型名称,例如:

var str: 记录

FIO:字符串[20];

电话:字符串[7];

结束;

仅在赋值语句中允许在赋值语句的左侧和右侧使用相同类型的记录名称时才能引用整个记录。 在所有其他情况下,操作单独的记录字段。 要引用单个记录组件,您必须指定记录的名称,并以点分隔,指定所需字段的名称。 这样的名称称为复合名称。 记录组件也可以是记录,在这种情况下,可分辨名称将不包含两个,而是包含更多名称。

使用 with append 运算符可以简化引用记录组件。 它允许您仅用字段名称替换表征每个字段的复合名称,并在连接语句中定义记录名称。

有时,单个记录的内容取决于其中一个字段的值。 在 Pascal 语言中,记录描述是允许的,由通用部分和变体部分组成。 变体部分是使用 case P ofconstruct 指定的,其中 P 是记录公共部分的字段名称。 该字段接受的可能值的列出方式与variant语句中的相同。 但是,与在变体语句中所做的那样指定要执行的操作不同,变体字段在括号中指定。 变体部分的描述以服务词结尾。 字段类型 P 可以在变量部分的标题中指定。 记录使用类型化常量进行初始化。

13. 套装

Pascal 语言中的集合概念是基于集合的数学概念:它是不同元素的有限集合。 枚举或区间数据类型用于构造具体的集合类型。 构成集合的元素类型称为基本类型。

使用功能词集来描述多重类型,例如:

类型 M = B 组;

其中 M 是复数类型,B 是基本类型。

变量是否属于复数类型可以直接在变量声明部分确定。

集合类型常量被写为基本类型元素或间隔的括号序列,用逗号分隔。

赋值 (:=)、联合 (+)、交集 (*) 和减法 (-) 操作适用于集合类型的变量和常量。 这些操作的结果是复数类型的值:

1) ['A','B'] + ['A','D'] 将给出 ['A','B','D'];

2) ['A'] * ['A','B','C'] 将给出 ['A'];

3) ['A','B','C'] - ['A','B'] 将给出 ['C']

操作适用于多个值:身份(=)、非身份(<>)、包含在(<=)中、包含(>=)。 这些操作的结果具有布尔类型:

1) ['A','B'] = ['A','C'] 将给出 FALSE;

2) ['A','B'] <> ['A','C'] 将给出 TRUE;

3) ['B'] <= ['B','C'] 将给出 TRUE;

4) ['C','D'] >= ['A'] 将给出 FALSE。

除了这些操作之外,为了处理集合类型的值,使用了 in 操作,它检查操作符号左侧的基本类型的元素是否属于操作符号右侧的集合. 此操作的结果是一个布尔值。

多个类型的值不能是 I/O 列表的元素。 在 Pascal 语言编译器的每个具体实现中,构建集合的基本类型的元素数量是有限的。

14. 文件。 文件操作

文件数据类型定义了相同类型组件的有序集合。

处理文件时,会执行 I/O 操作。 输入操作是将数据从外部设备传输到内存,输出操作是将数据从内存传输到外部设备。

文本文件

为了描述这些文件,有一个 Text 类型:

var TF1,TF2:文本;

组件文件

组件或类型化文件是具有其组件声明类型的文件。

类型 M = T 文件;

其中 M 是文件类型的名称;

T - 组件类型。 使用过程执行操作。

写入(f, X1,X2,...XK)

无类型文件

无类型文件允许您将计算机内存的任意部分写入磁盘并读取它们。

var f:文件;

1. 过程赋值(var F; FileName: String); 它将文件名映射到变量。

2. 程序关闭(varF);

它断开文件变量和外部磁盘文件之间的链接并关闭文件。

3.函数Eof(var F):布尔值;

{类型化或非类型化文件}

函数 Eof[(var F: Text)]: 布尔值;

{文本文件}

检查文件的结尾。

4. 程序擦除(var F);

删除与 F 关联的外部文件。

5.函数FileSize(var F):整数;

返回文件 F 的大小(以字节为单位)。

6.Function FilePos(varF): LongInt;

返回文件中的当前位置。

7. 过程重置(var F [: File; RecSize: Word]);

打开现有文件。

8. 过程重写(var F: File [; Recsize: Word]);

创建并打开一个新文件。

9. 过程 Seek(var F; N: LongInt);

将当前文件位置移动到指定组件。

10. 程序追加(var F: Text);

添加。

11.Function Eoln[(var F: Text)]: Boolean;

检查字符串的结尾。

12.过程Read(F,V1[,V2...,Vn]);

{类型化和非类型化文件}

过程 Read([var F: Text;] V1 [, V2..., Vn]);

{文本文件}

将文件组件读入变量。

13. 过程 Readln([var F: Text;] V1 [, V2..., Vn]);

读取文件中的一行字符,包括行尾标记,并移动到下一个字符的开头。

14. 函数 SeekEof[(var F: Text)]: 布尔值;

返回文件结束符号。 仅用于打开的文本文件。

15. 过程 Writeln([var F: Text;] [P1, P2..., Pn]);

{文本文件}

执行写入操作,然后在文件中放置一个行尾标记。

15. 模块。 模块类型

Pascal 中的单元 (UNIT) 是专门设计的子程序库。 与程序不同,模块不能自行启动执行,它只能参与构建程序和其他模块。

Pascal 中的模块是一个单独存储和独立编译的程序单元。

该模块的所有程序元素可分为两部分:

1) 供其他程序或模块使用的程序元素,这些元素在模块外称为可见的;

2) 仅对模块本身的操作所必需的软件元素,它们被称为不可见的(或隐藏的)。

单元<模块名称>; {模块标题}

接口

{模块可见程序元素的描述}

履行

{模块隐藏编程元素的描述}

开始

{模块元素初始化语句}

结束。

要引用模块中声明的变量,您必须使用由模块名称和变量名称组成的复合名称,并用点分隔。

禁止递归使用模块。 让我们列出模块的类型。

1.系统模块。

SYSTEM 模块为所有内置功能(如 I/O、字符串操作、浮点操作和动态内存分配)实现了较低级别的支持例程。

2.DOS模块。

Dos 模块实现了许多 Pascal 例程和函数,它们等效于最常用的 DOS 调用,例如 GetTime、SetTime、DiskSize 等。

3. CRT模块。

CRT 模块实现了许多功能强大的程序,可以完全控制 PC 的功能,例如屏幕模式控制、扩展键盘代码、颜色、窗口和声音。

4.图形模块。

使用此模块中包含的程序和功能,您可以在屏幕上创建各种图形。

5.叠加模块。

OVERLAY 模块允许您减少实模式 DOS 程序的内存需求。

16.引用数据类型。 动态记忆。 动态变量。 使用动态内存

静态变量(静态分配)是在程序中显式声明的变量,通过名称引用。 静态变量在内存中的位置是在编译程序时确定的。 与此类静态变量不同,Pascal 程序可以创建动态变量。 动态变量的主要属性是它们在程序执行期间被创建并为它们分配内存。

动态变量被放置在动态内存区域(堆区域)中。 动态变量没有在变量声明中明确指定,也不能通过名称引用。 使用指针和引用访问此类变量。

引用类型(指针)定义了一组值,这些值指向某种类型的动态变量,称为基类型。 引用类型变量包含内存中动态变量的地址。 如果基类型是一个未声明的标识符,那么它必须在类型声明的同一部分中声明为指针类型。

保留字 nil 表示具有不指向任何内容的指针值的常量。

让我们举一个动态变量描述的例子。

变量 p1, p2: ^real;

p3, p4: ^整数;

...

动态内存过程和函数

1. 过程 New{var p: Pointer)。

在动态内存区域中分配空间以容纳动态变量 p",并将其地址分配给指针 p。

2. 过程 Dispose(var p: Pointer)。

释放 New 过程为动态变量分配分配的内存,指针 p 的值变为未定义。

3. 过程 GetMem(var p: Pointer; size: Word)。

在堆区域中分配一段内存,将其起始地址分配给 p 指针,该段的大小(以字节为单位)由 size 参数指定。

4.过程FreeMem(varp: Pointer; size: Word)。

释放内存区域,其起始地址由p指针指定,大小由size参数指定。 指针值 p 变为未定义。

5. 过程 Mark{var p: Pointer) 在调用时将空闲动态内存段的起始地址写入指针 p。

6、Release(var p: Pointer)过程释放一段动态内存,从Mark过程写入指针p的地址开始,即清除调用Mark过程后占用的动态内存。

7.函数MaxAvail:Longint返回动态内存最长空闲段的字节长度。

8.函数MemAvail:Longint以字节为单位返回可用动态内存的总量。

9. SizeOf(X):Word 帮助函数返回 X 占用的大小(以字节为单位),其中 X 可以是任何类型的变量名,也可以是类型名。

17. 抽象数据结构

结构化数据类型,例如数组、集合和记录,是静态结构,因为它们的大小在程序的整个执行过程中不会改变。

通常要求数据结构在解决问题的过程中改变它们的大小。 这种数据结构称为动态的。 这些包括堆栈、队列、列表、树等。

使用数组、记录和文件来描述动态结构会导致计算机内存的浪费并增加解决问题的时间。

任何动态结构的每个组件都是包含至少两个字段的记录:一个字段类型为“指针”,第二个字段用于数据放置。 一般来说,一条记录可能包含的不是一个,而是几个指针和几个数据字段。 数据字段可以是变量、数组、集合或记录。

如果指向部分包含列表中一个元素的地址,则该列表称为单向(或单链接)。 如果它包含两个组件,则它是双重连接的。 您可以对列表执行各种操作,例如:

1) 向列表中添加一个元素;

2) 使用给定键从列表中删除一个元素;

3) 搜索具有给定键字段值的元素;

4)对列表的元素进行排序;

5) 将列表划分为两个或多个列表;

6) 将两个或多个列表合并为一个;

7) 其他操作。

但是,通常不会出现解决各种问题的所有操作的需要。 因此,根据需要应用的基本操作,存在不同类型的列表。 其中最流行的是堆栈和队列。

18. 堆栈

堆栈是一种动态数据结构,添加组件和删除组件都是从一端开始的,称为堆栈顶部。 堆栈的工作原理是 LIFO(后进先出)——“后进先出”。

通常对栈执行三种操作:

1)堆栈的初始形成(第一个组件的记录);

2) 向栈中添加一个组件;

3)组件的选择(删除)。

要形成堆栈并使用它,您必须有两个“指针”类型的变量,第一个确定堆栈的顶部,第二个是辅助的。

例子。 编写一个程序,形成一个堆栈,向其中添加任意数量的组件,然后读取所有组件。

程序堆栈;

使用 Crt;

类型

阿尔法=字符串[10];

PComp = ^Comp;

比较 = 记录

SD:阿尔法;

pNext:PComp

结束;

VAR

pTop:PComp;

sc:阿尔法;

创建 ProcedureStack(var pTop: PComp; var sC: Alfa);

开始

新的(顶部);

pTop^.pNext:= 无;

pTop^.sD:= sC;

结束;

添加 ProcedureComp(var pTop: PComp; var sC: Alfa);

var pAux: PComp;

开始

新(pAux);

pAux^.pNext:= pTop;

pTop:=pAux;

pTop^.sD:= sC;

结束;

程序 DelComp(var pTop: PComp; var sC: ALFA);

开始

sC:= pTop^.sD;

pTop:= pTop^.pNext;

结束;

开始

清除;

writeln( 输入字符串 );

读入(SC);

CreateStack(pTop, sc);

重复

writeln( 输入字符串 );

读入(SC);

AddComp(pTop, sc);

直到 sC = 'END';

19. 队列

队列是一种动态数据结构,其中一个组件在一端添加并在另一端检索。 队列的工作原理是 FIFO(先进先出)——“先进先出”。

例子。 编写一个程序,形成一个队列,向其中添加任意数量的组件,然后读取所有组件。

程序队列;

使用 Crt;

类型

阿尔法=字符串[10];

PComp = ^Comp;

比较 = 记录

SD:阿尔法;

p下一个:PComp;

结束;

VAR

pBegin,pEnd:PComp;

sc:阿尔法;

创建 ProcedureQueue(var pBegin,pEnd: PComp; var

sc:阿尔法);

开始

新的(p开始);

pBegin^.pNext:= NIL;

pBegin^.sD:= sC;

pEnd:=pBegin;

结束;

过程 AddQueue(var pEnd: PComp; var sC:

α);

var pAux: PComp;

开始

新(pAux);

pAux^.pNext:= NIL;

pEnd^.pNext:= pAux;

pEnd:= pAux;

pEnd^.sD:= sC;

结束;

过程 DelQueue(var pBegin: PComp; var sC:

α);

开始

sC:=pBegin^.sD;

pBegin:= pBegin^.pNext;

结束;

开始

清除;

writeln( 输入字符串 );

读入(SC);

CreateQueue(pBegin, pEnd, sc);

重复

writeln( 输入字符串 );

读入(SC);

添加队列(pEnd,sc);

直到 sC = 'END';

20. 树数据结构

树状数据结构是元素节点的有限集合,它们之间存在关系——源节点和生成节点之间的连接。

如果我们使用 N. Wirth 提出的递归定义,那么基类型为 t 的树数据结构要么是一个空结构,要么是一个类型为 t 的节点,其中一组具有基类型 t 的树结构的有限集合,称为子树,是联系。

接下来,我们给出操作树结构时使用的定义。

如果节点 y 位于节点 x 的正下方,则节点 y 称为节点 x 的直接后代,x 是节点 y 的直接祖先,即如果节点 x 位于第 i 层,则节点 y 相应地为位于第 (i + 1 ) 层。

树节点的最大级别称为树的高度或深度。 祖先不只有树的一个节点——它的根。

没有子节点的树节点称为叶子节点(或树的叶子)。 所有其他节点称为内部节点。 节点的直接子节点的数量决定了该节点的度数,给定树中节点的最大可能度数决定了树的度数。

祖先和后代不能互换,即原始与生成之间的联系仅在一个方向上起作用。

如果你从树的根到某个特定的节点,那么在这种情况下将遍历的树的分支数称为该节点的路径长度。 如果树的所有分支(节点)都是有序的,则称这棵树是有序的。

二叉树是树结构的一种特殊情况。 这些是每个孩子最多有两个孩子的树,称为左子树和右子树。 因此,二叉树是一种度数为二的树结构。

二叉树的排序由以下规则确定:每个节点都有自己的键域,并且对于每个节点,键值大于其左子树中的所有键,小于其右子树中的所有键。

度数大于 XNUMX 的树称为强分支树。

21. 树上的操作

此外,我们将考虑与二叉树相关的所有操作。 一、建树。

我们提出了一种构造有序树的算法。

1. 如果树为空,则将数据传输到树的根。 如果树不为空,则它的一个分支以不违反树顺序的方式下降。 结果,新节点成为树的下一个叶子。

2. 要将节点添加到已经存在的树中,可以使用上述算法。

3. 当从树中删除一个节点时,你应该小心。 如果要移除的节点是叶子,或者只有一个孩子,那么操作很简单。 如果要删除的节点有两个后代,则需要在其后代中找到一个可以放置在其位置的节点。 这是必要的,因为需要对树进行排序。

您可以这样做:将要删除的节点与左子树中键值最大的节点交换,或者与右子树中键值最小的节点交换,然后将所需节点作为叶子删除。

二、 搜索具有给定键字段值的节点。

执行此操作时,需要遍历树。 有必要考虑不同形式的树表示法:前缀、中缀和后缀。

问题来了:如何表示树的节点,以便使用它们最方便? 可以使用数组来表示树,其中每个节点由组合类型的值描述,组合类型具有字符类型的信息字段和引用类型的两个字段。 但这不是很方便,因为树有大量未预先确定的节点。 因此,描述树时最好使用动态变量。 那么每个节点都用一个相同类型的值来表示,其中包含对给定数量的信息字段的描述,并且对应的字段的数量必须等于树的度数。 通过引用 nil 来定义没有后代是合乎逻辑的。 然后,在 Pascal 中,二叉树的描述可能如下所示:

类型树链接 = ^树;

树=记录;

Inf: <数据类型>;

左、右:TreeLink;

结束。

22. 操作执行实例

1. 构造一棵具有 XNUMX 个最小高度节点的树,或者是一棵完全平衡的树(这种树的左右子树的节点数应该相差不超过一个)。

递归构造算法:

1)将第一个节点作为树的根;

2)nl个节点的左子树以同样的方式构建;

3)nr个节点的右子树以同样的方式构建;

nr = n - nl - 1

作为信息字段,我们将使用从键盘输入的节点编号。 实现此构造的递归函数将如下所示:

函数树(n:字节):TreeLink;

变体:TreeLink; nl,nr,x:字节;

开始

如果 n = 0 则 Tree:= nil

其他

开始

nl:= n 格 2;

nr = n - nl - 1;

writeln('输入顶点编号);

读入(x);

新的(t);

t^.inf:= x;

t^.left:= 树(nl);

t^.right:= 树(nr);

树:=t;

结束;

{树}

结束。

2. 在二叉树中,找到给定键域值的节点。 如果树中没有这样的元素,则将其添加到树中。

搜索过程(x: Byte; var t: TreeLink);

开始

如果 t = nil 那么

开始

新(t);

t^inf:= x;

t^.left:= 无;

t^.right:= 无;

结束

否则,如果 x < t^.inf 那么

搜索(x, t^.left)

否则,如果 x > t^.inf 那么

搜索(x, t^.right)

其他

开始

{处理找到的元素}

...

结束;

结束。

23. 图的概念。 表示图的方法

图是一对 G = (V,E),其中 V 是一组任意性质的对象,称为顶点,E 是一组对 ei = (vil, vi2)、vijOV,称为边。 在一般情况下,集合 V 和(或)族 E 可以包含无限数量的元素,但我们将只考虑有限图,即 V 和 E 都是有限的图。 如果 ei 中包含的元素的顺序很重要,则该图称为有向图,缩写为有向图,否则称为无向图。 有向图的边称为弧。

如果 e = ,则顶点 v 和 u 称为边的端点。 这里我们说边 e 与每个顶点 v 和 u 相邻(事件)。 顶点 v 和 和 也称为相邻(事件)。 在一般情况下,形式 e = ; 这样的边缘称为循环。

图顶点的度数是入射到给定顶点的边数,循环计数两次。

节点的权重是分配给给定节点的数字(实数、整数或有理数)(解释为成本、吞吐量等)。

图中的路径(或有向图中的路线)是顶点和边(或有向图中的弧)的交替序列,其形式为 v0, (v0,v1), v1,..., (vn -1,越南语),越南语。数字 n 称为路径长度。没有重复边的路径称为链;没有重复顶点的路径称为简单链。没有重复边的闭合路径称为循环(或

有向图中的轮廓); 没有重复顶点(第一个和最后一个除外) - 一个简单的循环。

如果图的任意两个顶点之间存在路径,则称为连通图,否则称为断开连接。

有多种表示图形的方法。

1. 发病矩阵。

这是一个 n x m 矩形矩阵,其中 n 是顶点数,m 是边数。

2. 邻接矩阵。

这是一个维度为 n × n 的方阵,其中 n 是顶点数。

3. 邻接(事件)列表。 表示一个数据结构

对于图的每个顶点,存储与其相邻的顶点列表。 该列表是一个指针数组,其中第 i 个元素包含指向与第 i 个顶点相邻的顶点列表的指针。

4. 列表列表。

它是一种树状数据结构,其中一个分支包含与每个分支相邻的顶点列表。

24. 各种图形表示

要将图表实现为关联列表,您可以使用以下类型:

类型列表 = ^S;

S=记录;

inf:字节;

下一个:列表;

结束;

那么图形定义如下:

vargr:List的array[1..n];

现在让我们转向图遍历过程。 这是一种辅助算法,可让您查看图形的所有顶点,分析所有信息字段。 如果我们深入考虑图的遍历,那么有两种算法:递归和非递归。

在 Pascal 中,深度优先遍历过程如下所示:

过程 Obhod(gr: Graph; k: Byte);

变量:图表; l:列表;

开始

新 [k]:= 假;

克:=克;

而 g^.inf <> k 做

g:= g^.下一个;

l:= g^.smeg;

虽然 l <> nil 开始

如果 nov[l^.inf] 那么 Obhod(gr, l^.inf);

l:= l^.下一个;

结束;

结束;

将图表示为列表的列表

可以使用列表列表定义图形,如下所示:

类型列表 = ^Tlist;

tlist=记录

inf:字节;

下一个:列表;

结束;

图 = ^TGpaph;

TGpaph = 记录

inf:字节;

smeg:列表;

下一个:图表;

结束;

当以广度遍历图时,我们选择一个任意顶点并一次查看与其相邻的所有顶点。

这是在伪代码中遍历宽度图的过程:

程序 Obhod2(v);

开始

队列 = O;

队列 <= v;

新 [v] = 假;

While queue <> O do

开始

p <= 队列;

为你在 spisok(p) 做

如果 new[u] 那么

开始

新 [u]:= 错误;

队列<=你;

结束;

结束;

结束;

25. Pascal 中的对象类型。 对象的概念、描述和使用

面向对象的编程语言具有三个主要特性:

1)封装。 将记录与操作这些记录的字段的过程和函数结合起来形成一种新的数据类型——对象;

2)继承。 对象的定义及其进一步用于构建子对象的层次结构,使与层次结构相关的每个子对象能够访问所有父对象的代码和数据;

3) 多态性。 为操作指定一个名称,然后在对象层次结构中上下共享,层次结构中的每个对象以适合它的方式执行该操作。

说到对象,我们介绍一种新的数据类型——对象。 对象类型是由固定数量的组件组成的结构。 每个组件要么是包含严格定义类型的数据的字段,要么是对对象执行操作的方法。

一个对象类型可以继承另一个对象类型的组件。 如果类型 T2 继承自类型 T1,则类型 T2 是类型 G 的子类型,类型 G 本身是类型 G2 的父级。

以下源代码提供了对象类型声明的示例。

类型

点=对象

X、Y:整数;

结束;

矩形 = 对象

A、B:T点;

程序初始化(XA,YA,XB,YB:整数);

过程复制(var R:TRectangle);

程序移动(DX,DY:整数);

程序增长(DX,DY:整数);

过程相交(var R:TRectangle);

过程联合(var R:TRectangle);

函数包含(P:点):布尔值;

结束;

与其他类型不同,对象类型只能在程序或模块范围最外层的类型声明部分中声明。 因此,对象类型不能在变量声明部分或过程、函数或方法块内声明。

文件类型组件类型不能具有对象类型或任何包含对象类型组件的结构类型。

26. 继承

继承是从现有父类型生成新子类型的过程,而子从父接收(继承)其所有字段和方法。

在这种情况下,后代类型称为继承人或子类型。 子类型继承自的类型称为父类型。

继承的字段和方法可以原封不动地使用,也可以重新定义(修改)。

N. Wirth 在他的语言中,Pascal 力求最大限度地简化,因此他没有通过引入继承关系来使其复杂化。 因此,Pascal 中的类型不能继承。

然而,Turbo Pascal 7.0 扩展了这种语言以支持继承。 一个这样的扩展是与记录相关的新数据结构类别,但功能更强大。 这个新类别中的数据类型是使用新的保留字 Object 定义的。 语法与定义记录的语法非常相似:

Type

<类型名称> = 对象 [(<父类型名称>)]

([<范围>]

<字段和方法的描述>)+

结束;

括号中的语法结构后面的“+”号表示该结构在本描述中必须出现一次或多次。

范围是以下关键字之一:

▪ 私人;

▪ 受保护;

▪ 公开。

范围表征程序的哪些部分,其描述遵循命名此范围的关键字的组件将可用。

有关组件范围的更多信息,请参阅问题 #28。

继承是程序开发中使用的强大工具。 它允许您在实践中实现问题的面向对象分解,使用语言来表达形成层次结构的类型对象之间的关系,并促进程序代码的重用。

27. 实例化对象

通过声明对象类型的变量或常量,或通过将标准 New 过程应用于“指向对象类型的指针”类型的变量来创建对象的实例。 生成的对象称为对象类型的实例。

如果一个对象类型包含虚方法,那么该对象类型的实例必须在调用任何虚方法之前通过调用构造函数来初始化。

分配对象类型的实例并不意味着实例的初始化。 一个对象由编译器生成的代码初始化,该代码在构造函数的调用和执行实际到达构造函数代码块中的第一条语句的点之间运行。

如果对象实例未初始化并且启用了范围检查(通过 {$R+} 指令),则对对象实例的虚拟方法的第一次调用会产生运行时错误。 如果 {$R-} 指令关闭了范围检查),那么第一次调用未初始化对象的虚拟方法可能会导致不可预知的行为。

强制初始化规则也适用于结构类型组件的实例。 例如:

VAR

注释:TStrField 的数组 [1..5];

一:整数

开始

对于 I:= 1 到 5 做

注释 [I].Init (1, I + 10, 40, 'first_name');

.

.

.

对于 I:= 1 到 5 执行 Comment [I].Done;

结束;

对于动态实例,初始化通常与放置有关,而清理与删除有关,这是通过 New 和 Dispose 标准过程的扩展语法实现的。 例如:

VAR

SP:StrFieldPtr;

开始

新的(SP,初始化(1, 1, 25, 'first_name');

SP^.Put('弗拉基米尔');

SP^.显示;

.

.

.

处置(SP,完成);

结束。

指向对象类型的指针与指向任何父对象类型的指针的赋值兼容,因此在运行时指向对象类型的指针可以指向该类型的实例或任何子类型的实例。

28. 组件和范围

bean 标识符的范围超出了对象类型。 此外,bean 标识符的范围通过实现对象类型及其后代的方法的过程、函数、构造函数和析构函数块扩展。 基于这些考虑,组件标识符在对象类型及其所有后代以及所有方法中必须是唯一的。

在对象类型声明中,方法头可以指定所描述的对象类型的参数,即使声明尚未完成。

对于包含所有有效范围的组件的类型声明,请考虑以下模式:

Type

<类型名称> = 对象 [(<父类型名称>)]

私做

<字段和方法的私有描述>

保护

<受保护的字段和方法描述>

公共

<字段和方法的公开描述>

结束;

Private 部分中描述的字段和方法只能在包含其声明的模块中使用,而不能在其他任何地方使用。

受保护的字段和方法,即在受保护部分中描述的那些,对定义类型的模块和该类型的后代可见。

Public 部分中的字段和方法对其使用没有任何限制,可以在程序中可以访问此类型对象的任何地方使用。

类型声明的私有部分中描述的组件标识符的范围仅限于包含对象类型声明的模块(程序)。 换句话说,私有标识符 bean 就像包含对象类型声明的模块内的普通公共标识符一样,而在模块外部,任何私有 bean 和标识符都是未知且不可访问的。 通过将相关的对象类型放在同一个模块中,可以使这些对象访问彼此的私有组件,而这些私有组件将不为其他模块所知。

一、方法

对象类型内的方法声明对应于前向方法声明(forward)。 因此,在对象类型声明之后的某个地方,但在与对象类型声明的范围相同的范围内,必须通过定义其声明来实现方法。

对于过程和函数方法,定义声明采用普通过程或函数声明的形式,但在这种情况下,过程或函数标识符被视为方法标识符。

方法的定义描述总是包含一个标识符为 Self 的隐式参数,对应于对象类型的形式变量参数。 在方法块中,Self 表示其方法组件被指定为调用该方法的实例。 因此,对 Self 字段值的任何更改都会反映在实例中。

虚拟方法

默认情况下,方法是静态的,但除了构造函数之外,它们可以是虚拟的(通过在方法声明中包含 virtual 指令)。 编译器在编译过程中解析对静态方法调用的引用,而对虚拟方法的调用在运行时解析。 这有时称为后期绑定。

覆盖静态方法与更改方法头无关。 相反,虚拟方法覆盖必须保留顺序、参数类型和名称以及函数结果类型(如果有)。 此外,重新定义必须再次包含虚拟指令。

动态方法

Borland Pascal 支持称为动态方法的附加后期绑定方法。 动态方法与虚拟方法的区别仅在于它们在运行时分派的方式。 在所有其他方面,动态方法被认为等同于虚拟方法。

动态方法声明等同于虚拟方法声明,但动态方法声明必须包含动态方法索引,该索引紧跟在 virtual 关键字之后。 动态方法的索引必须是介于 1 和 656535 之间的整数常量,并且在对象类型或其祖先中包含的其他动态方法的索引中必须是唯一的。 例如:

过程 FileOpen(var Msg: TMessage); 虚拟100;

动态方法的重写必须匹配参数的顺序、类型和名称,并且与父方法的函数的结果类型完全匹配。 覆盖还必须包括一个虚拟指令,后跟在祖先对象类型中指定的相同动态方法索引。

30. 构造函数和析构函数

构造函数和析构函数是方法的特殊形式。 与 New 和 Dispose 标准过程的扩展语法结合使用,构造函数和析构函数具有放置和删除动态对象的能力。 此外,构造函数能够对包含虚方法的对象执行所需的初始化。 像所有方法一样,构造函数和析构函数可以被继承,并且对象可以包含任意数量的构造函数和析构函数。

构造函数用于初始化新创建的对象。 通常,初始化是基于作为参数传递给构造函数的值。 构造函数不能是虚拟的,因为虚拟方法的调度机制取决于首先初始化对象的构造函数。

以下是构造函数的一些示例:

构造函数 Field.Copy(var F: Field);

开始

自我:=F;

结束;

派生(子)类型的构造函数的主要操作几乎总是调用其直接父级的适当构造函数来初始化对象的继承字段。 执行此过程后,构造函数初始化对象的仅属于派生类型的字段。

析构函数与构造函数相反,用于在对象使用后进行清理。 通常,清理包括删除对象中的所有指针字段。

注意

析构函数可以是虚拟的,而且通常是。 析构函数很少有参数。 下面是一些析构函数的例子:

析构函数字段完成;

开始

FreeMem(姓名, 长度(姓名^) + 1);

结束;

析构函数 StrField.Done;

开始

FreeMem(值,Len);

现场完成;

结束;

子类型的析构函数,例如上面的 TStrField。 完成后,通常首先删除派生类型中引入的指针字段,然后,作为最后一步,调用直接父级的适当收集器-析构函数以删除对象的继承指针字段。

31. 析构函数

Borland Pascal 提供了一种特殊类型的方法,称为垃圾收集器(或析构函数),用于清理和删除动态分配的对象。 析构函数将删除对象的步骤与该类型对象所需的任何其他操作或任务相结合。 您可以为单个对象类型定义多个析构函数。

析构函数可以被继承,它们可以是静态的或虚拟的。 由于不同的终结器往往需要不同类型的对象,因此通常建议析构函数始终是虚拟的,以便为每种类型的对象执行正确的析构函数。

不需要为每个清理方法指定保留字析构函数,即使对象的类型定义包含虚方法。 析构函数实际上只对动态分配的对象起作用。

当清理动态分配的对象时,析构函数执行一个特殊的功能:它确保在动态分配的内存区域中始终释放正确数量的字节。 使用带有静态分配对象的析构函数不必担心; 事实上,通过不将对象的类型传递给析构函数,程序员剥夺了该类型对象在 Borland Pascal 中动态内存管理的全部好处。

当必须清除多态对象并且必须释放它们占用的内存时,析构函数实际上变成了它们自己。

多态对象是那些由于 Borland Pascal 的扩展类型兼容性规则而分配给父类型的对象。 术语“多态”是合适的,因为处理对象的代码在编译时“不知道”它最终必须处理的对象类型。 它唯一知道的是该对象属于对象层次结构,这些对象是指定对象类型的后代。

析构方法本身可以是空的并且只执行这个功能:

析构函数对象。完成;

开始

结束;

在这个析构函数中有用的不是它的主体的属性,但是,编译器会生成尾声代码来响应析构函数的保留字。 它就像一个模块,不导出任何东西,但通过在启动程序之前执行其初始化部分来完成一些无形的工作。 所有动作都发生在幕后。

32. 虚方法

如果方法的对象类型声明后跟新的保留字 virtual,则该方法变为虚拟。 如果父类型中的方法被声明为虚拟,则子类型中具有相同名称的所有方法也必须被声明为虚拟以避免编译器错误。

以下是示例工资单中正确虚拟化的对象:

类型

PEmployee = ^TEmployee;

员工=对象

姓名,标题:字符串[25];

率:真实;

构造函数 Init(AName, ATitle: String; ARate: Real);

函数GetPayAmount:真实; 虚拟的;

函数GetName:字符串;

函数GetTitle:字符串;

函数GetRate:真实;

程序展示; 虚拟的;

结束;

每小时 = ^每小时;

Thourly = 对象(TEmployee);

时间:整数;

构造函数 Init(AName, ATitle: String; ARate: Real;

时间:整数);

函数GetPayAmount:真实; 虚拟的;

函数GetTime:整数;

结束;

PSalaried = ^TSalaried;

TSalaried = 对象(TEmployee);

函数GetPayAmount:真实; 虚拟的;

结束;

P 委托 = ^T 委托;

TCommissioned = 对象(受薪);

佣金:真实;

销售金额:实物;

构造函数 Init(AName, ATitle: String; ARate,

ACommission, ASalesAmount: Real);

函数GetPayAmount:真实; 虚拟的;

结束;

构造函数是一种特殊类型的过程,它为虚方法机制做一些设置工作。 此外,必须在调用任何虚拟方法之前调用构造函数。 不先调用构造函数就调用虚方法会阻塞系统,编译器无法检查方法调用的顺序。

每个具有虚方法的对象类型都必须有一个构造函数。

必须在调用任何其他虚拟方法之前调用构造函数。 在未调用构造函数的情况下调用虚方法可能会导致系统锁定,并且编译器无法检查调用方法的顺序。

33.对象数据字段和形式化方法参数

方法及其对象共享一个公共范围这一事实的含义是,方法的形式参数不能与对象的任何数据字段相同。 这不是面向对象编程施加的一些新限制,而是 Pascal 一直拥有的旧范围规则。 这与禁止过程的形式参数与过程的局部变量相同。 考虑一个说明此过程错误的示例:

程序 CrunchIt(Cru​​nchee: MyDataRec, Crunchby,

错误代码:整数);

VAR

A、B:字符;

错误代码:整数;

开始

.

.

.

结束;

在包含局部变量 ErrorCode 声明的行上发生错误。 这是因为形参和局部变量的标识符相同。

一个过程的局部变量和它的形参共享一个共同的作用域,因此不能相同。 如果您尝试编译这样的内容,您将收到“错误 4:重复标识符”消息。 尝试将形式方法参数分配给此方法所属对象的字段名称时,也会发生相同的错误。

情况有些不同,因为将子例程头放在数据结构中是对 Turbo Pascal 创新的一种认可,但 Pascal 范围的基本原则没有改变。

在选择变量和参数标识符时,您仍然需要尊重特定的文化。 一些编程风格提供了命名类型字段的方法,以减少重复标识符的风险。 例如,匈牙利表示法建议字段名称以“m”前缀开头。

34. 封装

对象中代码和数据的组合称为封装。 原则上,可以提供足够多的方法,使对象的用户永远不会直接访问对象的字段。 其他一些面向对象的语言,例如 Smalltalk,需要强制封装,但 Borland Pascal 有一个选择。

例如,TEmployee 和 Thourly 对象的编写方式绝对不需要直接访问它们的内部数据字段:

类型

员工=对象

姓名,标题:字符串[25];

率:真实;

过程 Init(AName, ATitle: string; ARate: Real);

函数GetName:字符串;

函数GetTitle:字符串;

函数GetRate:真实;

函数GetPayAmount:真实;

结束;

Thourly = 对象(TEmployee)

时间:整数;

过程初始化(AName,ATitle:字符串;ARate:

Real, Atime: 整数);

函数GetPayAmount:真实;

结束;

这里只有四个数据字段:名称、标题、速率和时间。 GetName 和 GetTitle 方法分别显示工作人员的姓氏和职位。 GetPayAmount 方法使用费率,如果是工作 Tourly 和 Time 来计算支付给工作的金额。 不再需要直接引用这些数据字段。

假设存在一个 THourly 类型的 AnHourly 实例,我们可以使用一组方法来操作 AnHourly 的数据字段,如下所示:

每小时做一次

开始

Init (Aleksandr Petrov, Fork lift operator' 12.95, 62);

{显示姓氏、职位和金额

付款}

节目;

结束;

需要注意的是,访问一个对象的字段只能在这个对象的方法的帮助下进行。

35. 扩展对象

如果定义了派生类型,则继承父类型的方法,但如果需要,可以覆盖它们。 要覆盖继承的方法,只需声明一个与继承方法同名的新方法,但具有不同的主体和(如果需要)不同的参数集。

让我们定义一个 TEmployee 的子类型,它代表在以下示例中按小时计酬的员工:

常量

支付周期 = 26; {付款期}

加班阈值 = 80; { 付款期限 }

加班系数 = 1.5; { 每小时收费 }

类型

Thourly = 对象(TEmployee)

时间:整数;

过程初始化(AName,ATitle:字符串;ARate:

Real, Atime: 整数);

函数GetPayAmount:真实;

结束;

过程 THourly.Init(AName, ATitle: string;

Arate: Real, Atime: Integer);

开始

TEmployee.Init(AName, ATitle, ARate);

时间:= ATime;

结束;

函数 Thourly.GetPayAmount:真实;

VAR

超时:整数;

开始

加班:= 时间 - 加班阈值;

如果加班 > 0 那么

GetPayAmount:= RoundPay(超时阈值 * 费率

+

RateOverTime * OvertimeFactor

*速度)

其他

GetPayAmount:= RoundPay(时间*费率)

结束;

调用重写方法时,您必须确保派生对象类型包含父对象的功能。 此外,父方法的任何更改都会自动影响所有子方法。

重要提示:虽然可以覆盖方法,但不能覆盖数据字段。 一旦在对象层次结构中定义了数据字段,任何子类型都不能定义具有完全相同名称的数据字段。

36.对象类型的兼容性

继承在一定程度上修改了 Borland Pascal 的类型兼容性规则。 后代继承其所有祖先的类型兼容性。

这种扩展类型兼容性采用三种形式:

1)对象的实现之间;

2)在指向对象实现的指针之间;

3) 形参与实参之间。 类型兼容性仅从子扩展到父。

例如,TSalaried 是 TEmployee 的子代,而 TCommissioned 是 TSalaried 的子代。 考虑以下描述:

VAR

AnEmployee:TEmployee;

薪俸:TS薪俸;

P委托:T委托;

TEmployeePtr:^TEmployee;

TSalariedPtr:^TSalaried;

TCommissionedPtr:^TCommissioned;

在这些描述下,以下赋值运算符是有效的:

AnEmployee:=A薪;

工资:= 已委托;

TCommissionedPtr:= ACommissioned;

一般来说,类型兼容规则如下:源必须能够完全填满接收器。 派生类型包含其父类型由于继承属性而包含的所有内容。 因此,派生类型的大小不小于父类型的大小。 将父对象分配给子对象可能会使父对象的某些字段未定义,这是危险的,因此是非法的。

在赋值语句中,只有两种类型共有的字段才会从源复制到目标。 在赋值运算符中:

AnEmployee:= ACommissioned;

只有来自 ACommissioned 的 Name、Title 和 Rate 字段将被复制到 AnEmployee,因为这些是 TCommissioned 和 TEmployee 之间共享的唯一字段。 类型兼容性也适用于指向对象类型的指针,并遵循与对象实现相同的一般规则。 指向子的指针可以分配给指向父的指针。 鉴于前面的定义,以下指针分配是有效的:

TSalariedPtr:= TCommissionedPtr;

TEmployeePtr:= TSalariedPtr;

TEmployeePtr:= PCommissionedPtr;

给定对象类型的形式参数(值或变量参数)可以将其自己类型的对象或所有子类型的对象作为其实际参数。 如果您定义这样的过程标头:

程序 CalcFedTax(受害者:TSalaried);

那么实际的参数类型可以是 TSalaried 或 TCommissioned,但不能是 TEmployee。 受害者也可以是可变参数。 在这种情况下,遵循相同的兼容性规则。

值参数是指向作为参数传递的实际对象的指针,可变参数是实际参数的副本。 此副本仅包括属于形式值参数类型的那些字段。 这意味着将实参转换为形参的类型。

37. 关于汇编

曾几何时,汇编程序是一种语言,不知道它不可能使计算机做任何有用的事情。 渐渐地情况发生了变化。 出现了更方便的与计算机通信的方式。 但与其他语言不同,汇编程序并没有死;而且,它原则上不能这样做。 为什么? 为了寻找答案,我们将尝试了解汇编语言的一般含义。

简而言之,汇编语言是机器语言的符号表示。 最低硬件级别的机器中的所有进程仅由机器语言的命令(指令)驱动。 从中可以清楚地看出,尽管有通用名称,但每种计算机的汇编语言都是不同的。 这也适用于用汇编程序编写的程序的外观,以及这种语言所反映的思想。

如果没有汇编程序的知识,就不可能真正解决与硬件相关的问题(或者甚至更多与硬件相关的问题,例如提高程序的速度)。

程序员或任何其他用户可以使用任何高级工具,直到程序来构建虚拟世界,甚至可能甚至不怀疑计算机实际上执行的不是编写程序的语言的命令,而是它们转换后的表示以完全不同的语言——机器语言的枯燥乏味的序列命令的形式。 现在想象这样一个用户有一个非标准的问题。 例如,他的程序必须使用一些不寻常的设备或执行其他需要了解计算机硬件原理的操作。 无论程序员编写程序的语言多么好,他都离不开汇编程序。 几乎所有高级语言的编译器都包含将其模块与汇编器中的模块连接起来或支持访问汇编器编程级别的方法,这并非巧合。

计算机由多个物理设备组成,每个设备都连接到一个称为系统单元的单元。

38.微处理器的软件模型

在当今的计算机市场上,有各种各样不同类型的计算机。 因此,可以假设消费者会有一个问题 - 如何评估特定类型(或型号)计算机的功能及其与其他类型(型号)计算机的显着特征。

仅考虑计算机的框图是不够的,因为它在不同的机器中根本不同:所有计算机都有 RAM、处理器和外部设备。

计算机作为单一机制运行的方式、手段和资源是不同的。

为了将所有表征计算机的功能性程序控制属性的概念结合在一起,有一个特殊的术语 - 计算机体系结构。

计算机体系结构的概念第一次随着第三代机器的出现而被提及,进行对比评测。

只有在找出计算机的哪些部分是可见的并且可以用这种语言进行编程之后,才开始学习任何计算机的汇编语言是有意义的。 这就是所谓的计算机程序模型,其中一部分是微处理器程序模型,它包含32个寄存器,或多或少可供程序员使用。

这些寄存器可以分为两大类:

1)16个用户寄存器;

2) 16 个系统寄存器。

汇编语言程序大量使用寄存器。 大多数寄存器都有特定的功能用途。

除了上面列出的寄存器之外,处理器开发人员还在软件模型中引入了额外的寄存器,旨在优化某些类型的计算。 因此,在英特尔公司的 Pentium Pro (MMX) 处理器家族中,引入了英特尔的 MMX 扩展。 它包括 8 个 (MM0-MM7) 64 位寄存器,并允许您对成对的几种新数据类型执行整数运算:

1) 八个压缩字节;

2)四个压缩词;

3)两个双字;

4)四字;

换句话说,使用一条 MMX 扩展指令,程序员可以例如将两个双字相加。 物理上,没有添加新的寄存器。 MM0-MM7 是 64 位 FPU(浮点单元 - 协处理器)寄存器堆栈的尾数(低 80 位)。

此外,目前还有以下编程模型的扩展 - 3DNOW! 来自 AMD; SSE、SSE2、SSE3、SSE4。 AMD 和 Intel 处理器都支持最后 4 个扩展。

39. 用户注册

顾名思义,之所以调用用户寄存器,是因为程序员在编写程序时可以使用它们。 这些寄存器包括:

1)32个XNUMX位的寄存器可供程序员用来存储数据和地址(它们也被称为通用寄存器(RON)):

▪ eax/ax/ah/al;

▪ ebx/bx/bh/bl;

▪ edx/dx/dh/dl;

▪ ecx/cx/ch/cl;

▪ ebp/bp;

▪ ESI/SI;

▪ 电子数据交换/数据交换;

▪ 特别/特别。

2)六个段寄存器:

▪ CS;

▪ ds;

▪ SS;

▪ 英语;

▪fs;

▪gs;

3) 状态和控制寄存器:

▪ 标志寄存器eflags/flags;

▪ 命令指针寄存器eip/ip。

下图显示了微处理器的主要寄存器:

通用寄存器

40. 一般登记册

该组的所有寄存器都允许您访问它们的“较低”部分。 只有这些寄存器的低 16 位和 8 位部分可用于自寻址。 这些寄存器的高 16 位不可用作独立对象。

让我们列出属于通用寄存器组的寄存器。 由于这些寄存器物理上位于算术逻辑单元 (ALU) 内部的微处理器中,因此它们也称为 ALU 寄存器:

1) eax/ax/ah/al(累加器寄存器)- 电池。 用于存储中间数据。 在某些命令中,该寄存器的使用是强制性的;

2)ebx/bx/bh/bl(基址寄存器)——基址寄存器。 用于存储某个对象在内存中的基地址;

3) ecx/cx/ch/cl(计数寄存器)——计数器寄存器。 它用于执行某些重复操作的命令中。 它的使用往往是隐含的,隐藏在相应命令的算法中。

例如,循环组织命令,除了将控制权转移到位于某个地址的命令之外,还对ecx/cx寄存器的值进行分析和减XNUMX;

4)edx/dx/dh/dl(数据寄存器)——数据寄存器。

就像 eax/ax/ah/al 寄存器一样,它存储中间数据。 有些命令需要使用它; 对于某些命令,这是隐式发生的。

以下两个寄存器用于支持所谓的链操作,即顺序处理元素链的操作,每个元素的长度可以是 32、16 或 8 位:

1)esi/si(源索引寄存器)——源索引。 链操作中的这个寄存器包含源链中元素的当前地址;

2) edi/di(Destination Index register)——接收者(recipient)的索引。 链操作中的该寄存器包含目标链中的当前地址。

在硬件和软件层面的微处理器架构中,都支持栈这样的数据结构。 为了在微处理器指令系统中使用堆栈,有特殊的命令,在微处理器软件模型中,有专门的寄存器:

1)esp/sp(堆栈指针寄存器)——堆栈指针寄存器。 包含指向当前堆栈段中堆栈顶部的指针。

2) ebp/bp (Base Pointer register)——栈帧基指针寄存器。 旨在组织对堆栈内数据的随机访问。

对某些指令使用寄存器的硬固定可以更紧凑地对其机器表示进行编码。 了解这些特性将在必要时节省至少几个字节的程序代码占用的内存。

41. 段寄存器

微处理器软件模型中有六个段寄存器:cs、ss、ds、es、gs、fs。

它们的存在是由于英特尔微处理器的组织和使用 RAM 的特殊性。 这在于微处理器硬件以三部分的形式支持程序的结构组织,称为段。 因此,这种内存组织称为分段。

为了指示程序在特定时间点可以访问的段,需要使用段寄存器。 事实上(稍加修正)这些寄存器包含相应段开始的内存地址。 处理机器指令的逻辑是这样构造的,即在获取指令、访问程序数据或访问堆栈时,隐式使用明确定义的段寄存器中的地址。

微处理器支持以下类型的段。

1.代码段。 包含程序命令。 要访问这个段,就要用到cs寄存器(代码段寄存器)——段代码寄存器。 它包含微处理器可以访问的机器指令段的地址(即,这些指令被加载到微处理器流水线中)。

2.数据段。 包含程序处理的数据。 为了访问这个段,使用了ds寄存器(数据段寄存器)——一个段数据寄存器,存储当前程序的数据段的地址。

3.堆栈段。 该段是称为堆栈的内存区域。 微处理器根据以下原则组织与堆栈的工作:首先选择写入该区域的最后一个元素。 为了访问这个段,使用了 ss 寄存器(堆栈段寄存器)——堆栈段寄存器包含堆栈段的地址。

4.附加数据段。 隐含地,执行大多数机器指令的算法假设它们处理的数据位于数据段中,其地址在 ds 段寄存器中。 如果程序没有足够的一个数据段,则它有机会使用另外三个额外的数据段。 但与主数据段不同,主数据段的地址包含在 ds 段寄存器中,当使用附加数据段时,必须在命令中使用特殊的段重定义前缀明确指定它们的地址。 附加数据段的地址必须包含在寄存器 es、gs、fs(扩展数据段寄存器)中。

42. 状态和控制寄存器

微处理器包括几个寄存器,这些寄存器不断地包含有关微处理器本身和其指令当前加载到流水线的程序的状态的信息。 这些寄存器包括:

1)标志寄存器eflags/flags;

2) eip/ip 命令指针寄存器。

使用这些寄存器,您可以获得有关命令执行结果的信息并影响微处理器本身的状态。 让我们更详细地考虑这些寄存器的用途和内容。

1. eflags/flags(标志寄存器)——标志寄存器。 eflags/flags 的位深度为 32/16 位。 该寄存器的各个位具有特定的功能用途,称为标志。 该寄存器的下部完全类似于 i8086 的标志寄存器。

根据使用方式的不同,eflags/flags 寄存器的标志可以分为三组:

1) 八个状态标志。

这些标志可能会在机器指令执行后发生变化。 eflags 寄存器的状态标志反映了算术或逻辑运算执行结果的细节。 这使得分析计算过程的状态并使用条件跳转命令和子程序调用对其进行响应成为可能。

2) 一个控制标志。

表示为 df(目录标志)。 它位于 eflags 寄存器的第 10 位,供链式命令使用。 df 标志的值决定了这些操作中逐元素处理的方向:从字符串的开头到结尾(df = 0)或反之,从字符串的结尾到开头(df = 1)。 要使用 df 标志,有一些特殊的命令:cld(删除 df 标志)和 std(设置 df 标志)。

使用这些命令可以让您根据算法调整 df 标志,并确保在对字符串执行操作时计数器自动递增或递减。 3) 五个系统标志。

控制 I/O、可屏蔽中断、调试、任务切换和 8086 虚拟模式。不建议应用程序不必要地修改这些标志,因为这在大多数情况下会导致程序终止。

2. eip/ip(Instraction Pointer register)——指令指针寄存器。 eip/ip 寄存器为 32/16 位宽,包含要执行的下一条指令相对于当前指令段中 cs 段寄存器内容的偏移量。 程序员不能直接访问该寄存器,但其值由各种控制命令加载和更改,包括条件和无条件跳转、调用过程和从过程返回的命令。 中断的发生也会修改 eip/ip 寄存器。

43.微处理器系统寄存器

这些寄存器的名字表明它们在系统中执行特定的功能。 系统寄存器的使用受到严格监管。 是他们提供保护模式。 它们也可以被认为是微处理器架构的一部分,故意让其可见,以便合格的系统程序员可以执行最低级别的操作。

系统寄存器可分为三组:

1)四个控制寄存器;

控制寄存器组包括 4 个寄存器:

▪ cr0;

▪ cr1;

▪ cr2;

▪ cr3;

2)四个系统地址寄存器(也叫内存管理寄存器);

系统地址寄存器包括以下寄存器:

▪ 全局描述符表寄存器gdtr;

▪ 局部描述符表寄存器Idtr;

▪ 中断描述符表寄存器idtr;

▪ 16 位任务寄存器tr;

3) 八个调试寄存器。 这些包括:

▪ dr0;

▪ dr1;

▪ dr2;

▪ dr3;

▪ dr4;

▪ dr5;

▪ dr6;

▪ 博士7。

在汇编程序中编写程序不需要系统寄存器的知识,因为它们主要用于执行最底层的操作。 然而,软件开发的当前趋势(特别是考虑到高级语言的现代编译器的优化能力显着提高,通常生成的代码在效率上优于人类代码)正在缩小汇编程序的范围,以解决最底层的问题。级问题,其中上述寄存器的知识可能非常有用。

44. 控制寄存器

控制寄存器组包括四个寄存器:cr0、cr1、cr2、cr3。 这些寄存器用于一般系统控制。 控制寄存器仅适用于特权级别 0 的程序。

虽然微处理器有四个控制寄存器,但只有三个可用——不包括cr1,其功能尚未定义(保留以备将来使用)。

cr0 寄存器包含系统标志,这些标志控制微处理器的操作模式并在全局范围内反映其状态,而与正在执行的特定任务无关。

系统标志的目的:

1) pe(保护启用),位 0 - 启用保护模式。 该标志的状态指示微处理器在给定时间运行的两种模式中的哪一种——真实模式(pe = 0)或保护模式(pe = 1);

2) mp (Math Present),第 1 位 - 协处理器的存在。 始终为 1;

3) ts (Task Switched),位 3 - 任务切换。 处理器在切换到另一个任务时自动设置该位;

4) am(对齐掩码),第 18 位 - 对齐掩码。

该位启用(am = 1)或禁用(am = 0)对齐控制;

5) cd (Cache Disable),第 30 位 - 禁用高速缓存。

使用该位,您可以禁用(cd = 1)或启用(cd = 0)内部缓存(一级缓存)的使用;

6) pg (PaGing),第 31 位 - 启用 (pg = 1) 或禁用 (pg = 0) 分页。

该标志用于内存组织的分页模型。

cr2 寄存器用于 RAM 分页,用于记录当前指令访问当前不在内存中的内存页面中包含的地址时的情况。

在这种情况下,微处理器中出现异常编号 14,并且导致该异常的指令的线性 32 位地址被写入寄存器 cr2。 有了这个信息,异常处理程序14确定所需的页面,将其交换到内存中并恢复程序的正常操作;

cr3 寄存器也用于分页内存。 这就是所谓的一级页目录寄存器。 它包含当前任务页目录的 20 位物理基地址。 该目录包含 1024 个 32 位描述符,每个描述符都包含二级页表的地址。 反过来,每个二级页表包含 1024 个 32 位描述符,用于寻址内存中的页帧。 页框大小为 4 KB。

45. 系统地址寄存器

这些寄存器也称为内存管理寄存器。

它们旨在保护微处理器多任务模式下的程序和数据。 在微处理器保护模式下运行时,地址空间分为:

1) 全局——所有任务通用;

2) 本地 - 为每个任务分开。 该划分解释了微处理器体系结构中存在以下系统寄存器:

1)全局描述符表gdtr(Global Descriptor Table Register)寄存器,大小为48位,包含全局描述符表GDT的32位(bits 16-47)基地址和16位(bits 0-15) 限制值,即 GDT 表的字节大小;

2)局部描述符表寄存器ldtr(Local Descriptor Table Register),大小为16位,包含所谓的局部描述符表LDT的描述符选择器。 这个选择器是一个指向 GDT 的指针,它描述了包含本地描述符表 LDT 的段;

3)中断描述​​符表寄存器idtr(Interrupt Descriptor Table Register),大小为48位,包含一个32位(bits 16-47)的IDT中断描述符表基地址和一个16位(bits 0-15) 限制值,即IDT表的字节大小;

4)16位任务寄存器tr(Task Register),和ldtr寄存器一样,包含一个选择器,即指向GDT表中的描述符的指针。 此描述符描述当前的任务段状态 (TSS)。 该段是为系统中的每个任务创建的,具有严格规范的结构并包含任务的上下文(当前状态)。 TSS 段的主要目的是保存一个任务在切换到另一个任务时的当前状态。

46.调试寄存器

这是一组非常有趣的用于硬件调试的寄存器。 硬件调试工具最早出现在 i486 微处理器中。 在硬件方面,微处理器包含 XNUMX 个调试寄存器,但实际使用的只有 XNUMX 个。

寄存器 dr0、dr1、dr2、dr3 的宽度为 32 位,用于设置四个断点的线性地址。这种情况下使用的机制如下:将当前程序生成的任何地址与寄存器 dr0...dr3 中的地址进行比较,如果匹配,则生成编号为 1 的调试异常。

寄存器 dr6 称为调试状态寄存器。 该寄存器中的位根据导致最后一个异常编号 1 发生的原因进行设置。

我们列出了这些位及其用途:

1) b0 - 如果该位设置为 1,则最后一个异常(中断)是由于到达寄存器 dr0 中定义的检查点而发生的;

2) b1 - 类似于 b0,但用于寄存器 dr1 中的检查点;

3) b2 - 类似于 b0,但用于寄存器 dr2 中的检查点;

4) b3 - 类似于 b0,但用于寄存器 dr3 中的检查点;

5) bd (bit 13) - 用于保护调试寄存器;

6) bs (bit 14) - 如果异常 1 是由 eflags 寄存器中的标志 tf = 1 的状态引起的,则设置为 1;

7) 如果异常 15 是由切换到 TSS t = 1 中设置的陷阱位的任务引起的,则 bt(位 1)设置为 1。该寄存器中的所有其他位均填充为零。异常处理程序1,根据dr6的内容,必须确定异常发生的原因并采取必要的操作。

寄存器 dr7 称为调试控制寄存器。 它包含四个调试断点寄存器中的每一个的字段,允许您指定应在以下条件下生成中断:

1)检查点注册位置——仅在当前任务或任何任务中。 这些位占用寄存器 dr8 的低 7 位(每个断点(实际上是一个断点)分别由寄存器 dr2、drl、dr0、dr2 设置 3 位)。

每对的第一位就是所谓的局部分辨率; 如果断点在当前任务的地址空间内,设置它会告诉断点生效。

每对中的第二位定义全局权限,表示给定断点在系统中所有任务的地址空间内有效;

2) 启动中断的访问类型:仅在获取命令、写入或写入/读取数据时。 确定中断发生的这种性质的位位于该寄存器的上部。 大多数系统寄存器都可以通过编程方式访问。

47. 汇编程序的结构

汇编语言程序是称为内存段的内存块的集合。 一个程序可能由这些块段中的一个或多个组成。 每个段包含一组语言句子,每个句子占据单独的程序代码行。

汇编语句有四种类型。

作为机器指令的符号对应物的命令或指令。

在翻译过程中,汇编指令被转换为微处理器指令集的相应命令。 通常,一条汇编程序指令对应于一条微处理器指令,一般来说,这对于低级语言来说是典型的。

下面是一个将存储在 eax 寄存器中的二进制数加一的指令示例:

公司

▪ 宏命令- 以某种方式格式化的节目文本句子,在广播期间被其他句子替换。

宏的一个示例是以下程序结束宏:

退出宏

movax,4c00h

int 21h

终结点

▪ 指令,是指示汇编器翻译器执行某些操作的指令。

指令在机器表示中没有对应物; 例如,这里是设置列表文件标题的 TITLE 指令:%TITLE "Listing 1"

▪ 包含任何字符的注释行,包括俄语字母表中的字母。注释会被译者忽略。例子:

; 这一行是注释

48. 汇编语法

组成程序的句子可以是对应于命令、宏、指令或注释的句法结构。 为了让汇编翻译器识别它们,它们必须按照一定的句法规则构成。 为此,最好使用语言语法的正式描述,如语法规则。 以这种方式描述编程语言的最常见方法是语法图和扩展的 Backus-Naur 形式。 使用语法图时,请注意遍历的方向,如箭头所示。 句法图反映了翻译器在解析程序输入语句时的逻辑。

有效字符:

1) 所有拉丁字母:A - Z、a - z;

2) 0 到 9 的数字;

3)标志? @, $, &;

4) 分隔符。

令牌如下。

1. 标识符——用于指定操作码、变量名和标签名的有效字符序列。 标识符不能以数字开头。

2. 字符链——用单引号或双引号括起来的字符序列。

3. 整数。

可能的汇编语句类型。

1.算术运算符。 这些包括:

1) 一元“+”和“-”;

2)二进制“+”和“-”;

3) 乘法“*”;

4)整数除法“/”;

5) 获得除法的余数“mod”。

2. 移位运算符将表达式移位指定的位数。

3.比较运算符(返回“true”或“false”)旨在形成逻辑表达式。

4. 逻辑运算符对表达式执行按位运算。

5.索引运算符[]。

6. ptr 类型重定义运算符用于重新定义或限定表达式定义的标签或变量的类型。

7. 段重定义运算符“:”(冒号)使物理地址相对于指定的段组件计算。

8.结构类型命名运算符“.” (dot) 如果出现在表达式中,也会导致编译器执行某些计算。

9、获取表达式seg地址的段分量的操作符返回表达式段的物理地址,可以是标签、变量、段名、组名或一些符号名。

10. 获取表达式偏移量的运算符允许您获取表达式相对于定义表达式的段的开头的偏移量(以字节为单位)。

49. 分割指令

分段是与模块化编程概念相关的更通用机制的一部分。 它涉及由编译器创建的目标模块设计的统一,包括来自不同编程语言的目标模块。 这允许您组合用不同语言编写的程序。 SEGMENT 指令中的操作数旨在实现此类联合的各种选项。

让我们更详细地考虑它们。

1.段对齐属性(对齐类型)告诉链接器确保段的开头放置在指定的边界上:

1) BYTE - 不执行对齐;

2) WORD - 段的起始地址是 0 的倍数,即物理地址的最后一位(最低有效位)为 XNUMX(与字边界对齐);

3) DWORD - 段的起始地址是四的倍数;

4) PARA - 段的起始地址是 16 的倍数;

5) PAGE - 段的起始地址是 256 的倍数;

6) MEMPAGE - 段的起始地址是 4 KB 的倍数。

2、combine段属性(组合类型)告诉链接器如何组合不同模块的同名段:

1) PRIVATE - 该段不会与该模块之外的其他同名段合并;

2) PUBLIC - 强制链接器连接所有同名段;

3) COMMON - 具有相同地址的所有同名段;

4) AT xxxx——将段定位在段落的绝对地址处;

5) STACK - 堆栈段的定义。

3. 段类属性(类类型)是一个带引号的字符串,它帮助链接器在从多个模块段组装程序时确定适当的段顺序。

4.段大小属性:

1) USE16 - 这意味着该段允许 16 位寻址;

2) USE32 - 该段将是 32 位的。 需要有某种方法来弥补这种不可能性。

直接控制段的放置和组合。 为此,他们开始使用指令来指定 MODEL 内存模型。 该指令将段(在使用简化分段指令的情况下,具有预定义名称)与段寄存器绑定(尽管您仍然必须显式初始化 ds)。

MODEL 指令的强制参数是内存模型。 该参数定义了 POU 的内存分段模型。 假设程序模块只能有某些类型的段,这些段由我们前面提到的简化段描述指令定义。

50.机器指令结构

机器命令是对微处理器的指示,根据某些规则编码,以执行某些操作或动作。 每个命令都包含定义:

1)怎么办?

2) 需要对其进行操作的对象(这些元素称为操作数);

3)怎么办?

机器指令的最大长度为 15 个字节。

1. 前缀。

可选的机器指令元素,每个为 1 字节或可以省略。 在内存中,前缀位于命令之前。 前缀的目的是修改命令执行的操作。 应用程序可以使用以下类型的前缀:

1) 段替换前缀;

2)地址位长前缀指定地址位长(32位或16位);

3)操作数位长前缀与地址位长前缀类似,但表示命令所使用的操作数位长(32位或16位);

4)重复前缀与链式命令一起使用。

2.操作码。

描述命令执行的操作的必需元素。

3.寻址方式字节modr/m。

该字节的值决定了使用的操作数地址形式。 操作数可以在内存中的一个或两个寄存器中。 如果操作数在内存中,则 modr/m 字节指定组件(偏移量、基址和索引寄存器)

用于计算其有效地址。 modr/m 字节由三个字段组成:

1)mod字段决定了操作数的地址在指令中占用的字节数;

2) reg/cop 字段确定位于命令中代替第一个操作数的寄存器,或操作码的可能扩展;

3) r/m 字段与 mod 字段一起使用,并确定位于命令中第一个操作数位置的寄存器(如果 mod = 11),或者用于计算有效地址的基址和索引寄存器(连同命令中的偏移字段)。

4. 字节尺度-索引-基数(byte sib)。 用于扩展寻址操作数的可能性。 sib 字节由三个字段组成:

1)规模领域ss。 该字段包含索引组件索引的比例因子,它占据了 sib 字节的下 3 位;

2)索引字段。 用于存放索引寄存器号,用于计算操作数的有效地址;

3) 基础字段。 用于存储基址寄存器号,也用于计算操作数的有效地址。

5. 命令中的偏移量字段。

一个 8 位、16 位或 32 位有符号整数,全部或部分(根据上述考虑)表示操作数的有效地址的值。

6.立即操作数的字段。 表示 8- 的可选字段,

16 位或 32 位立即操作数。 当然,该字段的存在反映在 modr/m 字节的值中。

51. 指定指令操作数的方法

操作数在固件级别隐式设置

在这种情况下,指令明确地不包含操作数。 命令执行算法使用一些默认对象(寄存器、eflags 中的标志等)。

操作数在指令本身中指定(立即操作数)

操作数在指令代码中,也就是说,它是指令代码的一部分。 为了存储这样的操作数,指令中分配了一个长达 32 位的字段。 直接操作数只能是第二个(源)操作数。 目标操作数可以在内存中,也可以在寄存器中。 操作数在其中一个寄存器中。寄存器操作数由寄存器名称指定。 可以使用寄存器:

1)32位寄存器EAX、EBX、ECX、EDX、ESI、EDI、ESP、EBP;

2)16位寄存器AX、BX、CX、DX、SI、DI、SP、BP;

3)8位寄存器AH、AL、BH、BL、CH、CL、DH、

深度学习;

4)段寄存器CS、DS、SS、ES、FS、GS。 例如,命令 add ax,bx 将寄存器 ax 和 bx 的内容相加,并将结果写入 bx。 dec si 命令将 si 的内容减 1。

操作数在内存中

这是指定操作数的最复杂同时也是最灵活的方式。 它允许您实现以下两种主要类型的寻址:直接和间接。

反过来,间接寻址具有以下变体:

1)间接基址寻址; 它的另一个名字是寄存器间接寻址;

2) 带偏移的间接基址寻址;

3) 带偏移的间接索引寻址;

4)间接基索引寻址;

5) 带偏移的间接基索引寻址。

操作数是一个 I/O 端口

除了 RAM 地址空间,微处理器还维护一个 I/O 地址空间,用于访问 I/O 设备。 I/O 地址空间为 64 KB。 为该空间中的任何计算机设备分配地址。 此空间内的特定地址值称为 I/O 端口。 在物理上,I/O 端口对应一个硬件寄存器(不要与微处理器寄存器混淆),使用特殊的汇编指令 in 和 out 来访问它。

操作数在栈上

指令可能根本没有操作数,可能有一个或两个操作数。 大多数指令需要两个操作数,一个是源操作数,另一个是目标操作数。 重要的是,一个操作数可以位于寄存器或内存中,而第二个操作数必须位于寄存器中或直接位于指令中。 立即操作数只能是源操作数。 在双操作数机器指令中,可能有以下操作数组合:

1)注册——注册;

2)寄存器——内存;

3)内存——寄存器;

4)立即操作数——寄存器;

5)立即操作数——内存。

52.寻址方式

直接寻址

这是在内存中寻址操作数的最简单形式,因为有效地址包含在指令本身中,并且没有使用额外的源或寄存器来形成它。 有效地址直接取自机器指令偏移字段,可以是 8、16、32 位。 该值唯一标识数据段中的字节、字或双字。

直接寻址可以有两种类型。

相对直接寻址

用于条件跳转指令,表示相对跳转地址。 这种转换的相关性在于机器指令的偏移字段包含一个 8 位、16 位或 32 位值,作为指令操作的结果,该值将添加到ip/eip 指令指针寄存器。 作为这种相加的结果,获得了执行转移的地址。

绝对直接寻址

在这种情况下,有效地址是机器指令的一部分,但该地址仅由指令中偏移字段的值构成。 为了形成内存中操作数的物理地址,微处理器将该字段与移位了 4 位的段寄存器的值相加。 这种寻址的几种形式可以在汇编指令中使用。

间接基本(寄存器)寻址

通过这种寻址,操作数的有效地址可以在任何通用寄存器中,除了 sp / esp 和 bp / ebp (这些是用于处理堆栈段的特定寄存器)。 在命令的语法上,这种寻址模式通过将寄存器名称括在方括号 [] 中来表示。

带偏移的间接基址(寄存器)寻址

这种类型的寻址是对前一种寻址的补充,旨在访问具有相对于某个基地址的已知偏移量的数据。 这种寻址方式便于访问数据结构的元素,当预先知道元素的偏移量时,在程序开发阶段,必须动态计算结构的基(起始)地址,在程序执行阶段。

带偏移的间接索引寻址

这种寻址与带有偏移的间接基址寻址非常相似。 这里也使用通用寄存器之一来形成有效地址。 但是索引寻址有一个有趣的特性,它非常便于处理数组。 它与索引寄存器内容的所谓缩放的可能性有关。

间接基索引寻址

使用这种类型的寻址,有效地址由两个通用寄存器的内容之和形成:基址和索引。 这些寄存器可以是任何通用寄存器,并且经常使用索引寄存器内容的缩放。

带偏移量的间接基索引寻址

这种寻址是间接索引寻址的补充。 有效地址由三个部分的总和构成:基址寄存器的内容、索引寄存器的内容和指令中偏移字段的值。

53.数据传输命令

通用数据传输命令

该组包括以下命令:

1)mov是主要的数据传输命令;

2) xchg - 用于双向数据传输。

端口 I/O 命令

从根本上说,直接通过端口管理设备很容易:

1) 在累加器中,端口号——从带有端口号的端口输入到累加器;

2) out port, accumulator——将累加器的内容输出到带有端口号的端口。

数据转换命令

许多微处理器指令可以归属于该组,但它们中的大多数具有某些特性,需要将它们归属于其他功能组。

堆栈命令

该组是一组专门的命令,专注于组织灵活高效的堆栈工作。

栈是专门为临时存储程序数据而分配的一块内存区域。

堆栈有三个寄存器:

1) ss——堆栈段寄存器;

2) sp/esp——堆栈指针寄存器;

3) bp/ebp - 堆栈帧基指针寄存器。 为了组织使用堆栈的工作,有用于写入和读取的特殊命令。

1.压入源——将源值写入堆栈顶部。

2.弹出赋值——将栈顶的值写入目标操作数指定的位置。 因此,该值从堆栈顶部“移除”。

3. pusha - 对堆栈的组写入命令。

4. pushaw 几乎是 pusha 命令的同义词。 bitness 属性可以是 use16 或 use32。 R

5. pushad - 执行类似于 pusha 命令,但有一些特殊性。

以下三个命令执行上述命令的相反操作:

1) 罂粟;

2)罂粟;

3)流行。

下面描述的指令组允许您将标志寄存器保存在堆栈中,并将一个字或双字写入堆栈。

1. pushf - 将标志寄存器保存在堆栈上。

2. pushfw - 在堆栈上保存一个字大小的标志寄存器。 总是像 pushf 一样使用 use16 属性。

3. pushfd——根据段的位宽属性(即与pushf相同)将flags或eflags标志寄存器保存在堆栈上。

同样,以下三个命令执行与上述操作相反的操作:

1) 流行音乐;

2) 流行音乐;

3) 流行音乐。

54.算术命令

此类命令适用于两种类型:

1)整数二进制数,即用二进制数系统编码的数字。

十进制数是数字信息的一种特殊表示形式,它是根据一组四位对数字的每个十进制数字进行编码的原理。

微处理器根据二进制数相加规则执行操作数相加。

微处理器指令集中有三个二进制加法指令:

1)inc操作数——增加操作数的值;

2)addoperand1,operand2——加法;

3) adc 操作数 1,操作数 2 - 加法,考虑进位标志 cf。

无符号二进制数的减法

如果被减数大于减数,则差为正。 如果被减数小于被减数,就会出现问题:结果小于0,而这已经是有符号数了。

减去无符号数后,需要分析 CF 标志的状态。 如果设置为 1,则最高有效位已被借用,结果为二进制补码。

带有符号的二进制数的减法但是对于通过在附加代码中添加带有符号的数字的方式进行减法,有必要表示两个操作数 - 被减数和被减数。 结果也应被视为二进制补码值。 但是这里出现了困难。 首先,它们与操作数的最高有效位被视为符号位这一事实有关。

根据溢出标志的内容。 将其设置为 1 表示该大小的操作数的结果超出有符号数的范围(即最高有效位已更改),程序员必须采取措施纠正结果。

表示范围超出操作数标准位格的数的减法原理与加法相同,即使用进位标志 cf。 你只需要想象一下在一列中减去的过程,并正确地将微处理器指令与 sbb 指令结合起来。

无符号数相乘的命令是

多重因子_1

将数字与符号相乘的命令是

[imul 操作数_1,操作数_2,操作数_3]

div divisor 命令用于除无符号数。

idiv divisor 命令用于划分有符号数。

55. 逻辑命令

根据该理论,可以对语句(对位)执行以下逻辑操作。

1. 否定(逻辑非)- 对一个操作数的逻辑运算,其结果是原始操作数的值的倒数。

2. 逻辑加法(逻辑或)——对两个操作数的逻辑运算,如果一个或两个操作数为真(1),结果为“真”(1),如果两个操作数都是“假”(0)假(0)。

3. 逻辑乘法(逻辑与) - 对两个操作数的逻辑运算,仅当两个操作数都为真 (1) 时,其结果才为真 (1)。 在所有其他情况下,操作的值为“假”(0)。

4. 逻辑异加(逻辑异或)——对两个操作数进行逻辑运算,如果两个操作数中只有一个为真(1),则其结果为“真”(1),假(0),如果两个操作数都是假 (0) 或真 (1)。

4. 逻辑异加(逻辑异或)——对两个操作数进行逻辑运算,如果两个操作数中只有一个为真(1),则其结果为“真”(1),假(0),如果两个操作数都是假 (0) 或真 (1)。

以下一组支持处理逻辑数据的命令:

1)和operand_1、operand_2——逻辑乘法运算;

2)或operand_1、operand_2——逻辑加法运算;

3) xor operand_1,operand_2——逻辑异加运算;

4)测试operand_1,operand_2——“测试”操作(通过逻辑乘法)

5) 非操作数——逻辑否定的操作。

a) 将某些数字(位)设置为 1,使用命令或操作数_1、操作数_2;

b) 将某些数字(位)重置为 0,使用命令和操作数_1、操作数_2;

c) 应用命令异或操作数_1,操作数_2:

▪ 找出operand_1 和operand_2 中哪些位不同;

▪ 将指定位的状态反转到操作数_1 中。

命令 test operand_1,operand_2(检查操作数_1)用于检查指定位的状态。

该命令的结果是设置零标志 zf 的值:

1)如果zf = 0,则作为逻辑乘法的结果,得到一个零结果,即掩码的一个单位位,与operand1对应的单位位不匹配;

2) 如果zf = 1,则逻辑乘法产生非零结果,即掩码的至少一个单位位与操作数1的相应一位一致。

所有移位指令根据操作码将操作数字段中的位向左或向右移动。所有移位指令都具有相同的结构 - cop 操作数、移位计数器。

56.控制转移命令

接下来应该执行哪条程序指令,微处理器从 cs: (e) ip 寄存器对的内容中学习:

1)cs——代码段寄存器,包含当前代码段的物理地址;

2) eip/ip——指令指针寄存器,它包含下一条要执行的指令在内存中的偏移值。

无条件跳转

需要修改的内容取决于:

1)关于无条件分支指令中操作数的类型(near or far);

2)从在转换地址之前指定一个修饰符; 在这种情况下,跳转地址本身可以直接在指令中(直接跳转),也可以在内存寄存器中(间接跳转)。

修饰符值:

1) near ptr - 直接过渡到标签;

2) far ptr - 直接转换到另一个代码段中的标签;

3) word ptr - 间接过渡到标签;

4) dword ptr - 间接转换到另一个代码段中的标签。

jmp 无条件跳转指令

jmp [修饰符] jump_address

过程或子程序是分解某些任务的基本功能单元。 一个过程是一组命令。

条件跳转

微处理器有 18 条条件跳转指令。 这些命令允许您检查:

1)有符号操作数之间的关系(“多即是少”);

2) 无符号操作数之间的关系

(“较高低”);

3)算术标志ZF、SF、CF、OF、PF(但不是AF)的状态。

条件跳转指令的语法相同:jcc jump label

cmp compare 命令有一种有趣的工作方式。 它与减法命令完全相同 - suboperand_1,operand_2。

cmp 命令与 sub 命令一样,会减去操作数并设置标志。 它唯一不做的就是写减法的结果来代替第一个操作数。

cmp 命令语法 - cmp operand_1,operand_2(比较) - 比较两个操作数并根据比较结果设置标志。

循环的组织

您可以组织程序某个部分的循环执行,例如,使用控制命令的条件转移或无条件跳转命令 jmp:

1)循环过渡标签(Loop)——重复循环。 该命令允许您组织循环,类似于高级语言中的 for 循环,循环计数器自动递减;

2) loope/loopz 跳转标签

loope 和 loopz 命令是绝对同义词;

3)loopne/loopnz跳转标签

命令 loopne 和 loopnz 也是绝对同义词。 loope/loopz 和 loopne/loopnz 命令在它们的操作中是相互的。

作者:茨维特科娃 A.V.

我们推荐有趣的文章 部分 讲义、备忘单:

文化学。 婴儿床

国内外历史的主要日期和事件。 婴儿床

民法。 第二部分。 婴儿床

查看其他文章 部分 讲义、备忘单.

读和写 有帮助 对这篇文章的评论.

<< 返回

科技、新电子最新动态:

用于触摸仿真的人造革 15.04.2024

在现代科技世界,距离变得越来越普遍,保持联系和亲密感非常重要。萨尔大学的德国科学家最近在人造皮肤方面的进展代表了虚拟交互的新时代。萨尔大学的德国研究人员开发出了超薄膜,可以远距离传输触觉。这项尖端技术为虚拟通信提供了新的机会,特别是对于那些发现自己远离亲人的人来说。研究人员开发的超薄膜厚度仅为 50 微米,可以融入纺织品中并像第二层皮肤一样穿着。这些薄膜充当传感器,识别来自妈妈或爸爸的触觉信号,并充当将这些动作传递给婴儿的执行器。父母触摸织物会激活传感器,对压力做出反应并使超薄膜变形。这 ... >>

Petgugu全球猫砂 15.04.2024

照顾宠物通常是一项挑战,尤其是在保持房屋清洁方面。 Petgugu Global 初创公司推出了一种有趣的新解决方案,这将使猫主人的生活变得更轻松,并帮助他们保持家中干净整洁。初创公司 Petgugu Global 推出了一款独特的猫厕所,可以自动冲掉粪便,让你的家保持干净清新。这款创新设备配备了各种智能传感器,可以监控宠物的厕所活动并在使用后激活自动清洁。该设备连接到下水道系统,确保有效清除废物,无需业主干预。此外,该厕所还具有较大的可冲水存储容量,非常适合多猫家庭。 Petgugu 猫砂碗专为与水溶性猫砂一起使用而设计,并提供一系列附加功能 ... >>

体贴男人的魅力 14.04.2024

长期以来,女性更喜欢“坏男孩”的刻板印象一直很普遍。然而,英国莫纳什大学科学家最近进行的研究为这个问题提供了新的视角。他们研究了女性如何回应男性的情感责任和帮助他人的意愿。这项研究的结果可能会改变我们对男性对女性吸引力的理解。莫纳什大学科学家进行的一项研究得出了有关男性对女性吸引力的新发现。在实验中,女性看到了男性的照片,并附有关于他们在各种情况下的行为的简短故事,包括他们对遇到无家可归者的反应。一些人无视这名无家可归的人,而另一些人则帮助他,比如给他买食物。一项研究发现,与表现出同理心和善良的男性相比,表现出同理心和善良的男性对女性更具吸引力。 ... >>

来自档案馆的随机新闻

手机打印机 16.09.2003

日本电子制造商万代有限公司最近推出了一款便携式打印机,用于在带有内置摄像头的手机上打印半珍贵的图像。

数据传输是通过红外端口进行的。打印输出在一张有粘性的纸上,因此照片可以用作贴纸。

其他有趣的新闻:

▪ 全新经济型奥迪2.0 TFSI发动机

▪ 需要睡觉不要太难

▪ 袖珍核共振扫描仪

▪ 海洋绿色革命:浮游植物的增加

▪ 太阳风在火卫一上产生电荷

科技、新电子资讯

 

免费技术图书馆的有趣材料:

▪ 网站部分 农业工具和机制。 文章精选

▪ 文章 有一场比赛! 流行表达

▪ 文章 什么是模具? 详细解答

▪ 文章茶树。 传说、栽培、使用方法

▪ 文章 Autoguard 具有少量零件。 无线电电子电气工程百科全书

▪ 文章 LCD 和 LED 显示器的电源。 无线电电子电气工程百科全书

留下您对本文的评论:

Имя:


电子邮件(可选):


点评:





本页所有语言

主页 | 图书馆 | 用品 | 网站地图 | 网站评论

www.diagram.com.ua

www.diagram.com.ua
2000-2024