Delphi руководство оператора

Go Up to Fundamental Syntactic Elements Index

This topic describes syntax rules of forming Delphi expressions.

The following items are covered in this topic:

  • Valid Delphi Expressions
  • Operators
  • Function calls
  • Set constructors
  • Indexes
  • Typecasts

Contents

  • 1 Expressions
  • 2 Operators
    • 2.1 Arithmetic Operators
    • 2.2 Boolean Operators
    • 2.3 Complete Versus Short-Circuit Boolean Evaluation
    • 2.4 Logical (Bitwise) Operators
      • 2.4.1 Example
    • 2.5 String Operators
    • 2.6 Pointer Operators
    • 2.7 Set Operators
    • 2.8 Relational Operators
    • 2.9 Class and Interface Operators
    • 2.10 The @ Operator
    • 2.11 Operator Precedence
  • 3 Function Calls
  • 4 Set Constructors
  • 5 Indexes
  • 6 Typecasts
    • 6.1 Value Typecasts
    • 6.2 Variable Typecasts
  • 7 See Also

Expressions

An expression is a construction that returns a value. The following table shows examples of Delphi expressions:

X

variable

@X

address of the variable X

15

integer constant

InterestRate

variable

Calc(X, Y)

function call

X * Y

product of X and Y

Z / (1 - Z)

quotient of Z and (1 — Z)

X = 1.5

Boolean

C in Range1

Boolean

not Done

negation of a Boolean

['a', 'b', 'c']

set

Char(48)

value typecast

The simplest expressions are variables and constants (described in About Data Types (Delphi)). More complex expressions are built from simpler ones using operators, function calls, set constructors, indexes, and typecasts.

Operators

Operators behave like predefined functions that are part of the Delphi language. For example, the expression (X + Y) is built from the variables X and Y, called operands, with the + operator; when X and Y represent integers or reals, (X + Y) returns their sum. Operators include @, not, ^, *, /, div, mod, and, shl, shr, as, +, , or, xor, =, >, <, <>, <=, >=, in, and is.

The operators @, not, and ^ are unary (taking one operand). All other operators are binary (taking two operands), except that + and can function as either a unary or binary operator. A unary operator always precedes its operand (for example, -B), except for ^, which follows its operand (for example, P^). A binary operator is placed between its operands (for example, A = 7).

Some operators behave differently depending on the type of data passed to them. For example, not performs bitwise negation on an integer operand and logical negation on a Boolean operand. Such operators appear below under multiple categories.

Except for ^, is, and in, all operators can take operands of type Variant; for details, see Variant Types (Delphi).

The sections that follow assume some familiarity with Delphi data types; for more information, see About Data Types (Delphi).

For information about operator precedence in complex expressions, see Operator Precedence Rules, later in this topic.

Arithmetic Operators

Arithmetic operators, which take real or integer operands, include +, , *, /, div, and mod.

Binary Arithmetic Operators:

Operator Operation Operand Types Result Type Example

+

addition

integer, real

integer, real

X + Y

subtraction

integer, real

integer, real

Result -1

*

multiplication

integer, real

integer, real

P * InterestRate

/

real division

integer, real

real

X / 2

div

integer division

integer

integer

Total div UnitSize

mod

remainder

integer

integer

Y mod 6

Unary arithmetic operators:

Operator Operation Operand Type Result Type Example

+

sign identity

integer, real

integer, real

+7

sign negation

integer, real

integer, real

-X

The following rules apply to arithmetic operators:

  • The value of x / y is of type Extended, regardless of the types of x and y. For other arithmetic operators, the result is of type Extended whenever at least one operand is a real; otherwise, the result is of type Int64 when at least one operand is of type Int64; otherwise, the result is of type Integer. If an operand’s type is a subrange of an integer type, it is treated as if it were of the integer type.
  • The value of x div y is the value of x / y rounded in the direction of zero to the nearest integer.
  • The mod operator returns the remainder obtained by dividing its operands. In other words,
    x mod y = x - (x div y) * y.
  • A run-time error occurs when y is zero in an expression of the form x / y, x div y, or x mod y.

Boolean Operators

The Boolean operators not, and, or, and xor take operands of any Boolean type and return a value of type Boolean.

Boolean Operators:

Operator Operation Operand Types Result Type Example

not

negation

Boolean

Boolean

not (C in MySet)

and

conjunction

Boolean

Boolean

Done and (Total >0)

or

disjunction

Boolean

Boolean

A or B

xor

exclusive disjunction

Boolean

Boolean

A xor B

These operations are governed by standard rules of Boolean logic. For example, an expression of the form x and y is True if and only if both x and y are True.

Complete Versus Short-Circuit Boolean Evaluation

The compiler supports two modes of evaluation for the and and or operators: complete evaluation and short-circuit (partial) evaluation. Complete evaluation means that each conjunct or disjunct is evaluated, even when the result of the entire expression is already determined. Short-circuit evaluation means strict left-to-right evaluation that stops as soon as the result of the entire expression is determined. For example, if the expression A and B is evaluated under short-circuit mode when A is False, the compiler will not evaluate B; it knows that the entire expression is False as soon as it evaluates A.

Short-circuit evaluation is usually preferable because it guarantees minimum execution time and, in most cases, minimum code size. Complete evaluation is sometimes convenient when one operand is a function with side effects that alter the execution of the program.

Short-circuit evaluation also allows the use of constructions that might otherwise result in illegal run-time operations. For example, the following code iterates through the string S, up to the first comma.

while (I <= Length(S)) and (S[I] <> ',') do
begin
 ...
 Inc(I);
end;

In the case where S has no commas, the last iteration increments I to a value which is greater than the length of S. When the while condition is next tested, complete evaluation results in an attempt to read S[I], which could cause a run-time error. Under short-circuit evaluation, in contrast, the second part of the while condition (S[I] <> ',') is not evaluated after the first part fails.

Use the $B compiler directive to control evaluation mode. The default state is {$B}, which enables short-circuit evaluation. To enable complete evaluation locally, add the {$B+} directive to your code. You can also switch to complete evaluation on a project-wide basis by selecting Complete Boolean Evaluation in the Compiler Options dialog (all source units will need to be recompiled).

Note: If either operand involves a variant, the compiler always performs complete evaluation (even in the {$B} state).

Logical (Bitwise) Operators

The following logical operators perform bitwise manipulation on integer operands. For example, if the value stored in X (in binary) is 001101 and the value stored in Y is 100001, the statement:

Z := X or Y;

assigns the value 101101 to Z.

Logical (Bitwise) Operators:

Operator Operation Operand Types Result Type Example

not

bitwise negation

integer

integer

not X

and

bitwise and

integer

integer

X and Y

or

bitwise or

integer

integer

X or Y

xor

bitwise xor

integer

integer

X xor Y

shl

bitwise shift left

integer

integer

X shl 2

shr

bitwise shift right

integer

integer

Y shr I

The following rules apply to bitwise operators:

  • The result of a not operation is of the same type as the operand.
  • If the operands of an and, or, or xor operation are both integers, the result is of the predefined integer type with the smallest range that includes all possible values of both types.
  • The operations x shl y and x shr y shift the value of x to the left or right by y bits, which (if x is an unsigned integer) is equivalent to multiplying or dividing x by 2^y; the result is of the same type as x. For example, if N stores the value 01101 (decimal 13), then N shl 1 returns 11010 (decimal 26). Note that the value of y is interpreted modulo the size of the type of x. Thus for example, if x is an integer, x shl 40 is interpreted as x shl 8 because an integer is 32 bits and 40 mod 32 is 8.

Example

If x is a negative integer, the shl and shr operations are made clear in the following example:

var
  x: integer;
  y: string;

...
begin
  x := -20;
  x := x shr 1;
  //As the number is shifted to the right by 1 bit, the sign bit's value replaced is with 0 (all negative numbers have the sign bit set to 1). 
  y := IntToHex(x, 8);
  writeln(y);
  //Therefore, x is positive.
  //Decimal value: 2147483638
  //Hexadecimal value: 7FFFFFF6
  //Binary value: 0111 1111 1111 1111 1111 1111 1111 0110
end.

String Operators

The relational operators =, <>, <, >, <=, and >= all take string operands (see Relational operators later in this section). The + operator concatenates two strings.

String Operators:

Operator Operation Operand Types Result Type Example

+

concatenation

string, packed string, character

string

S + '.'

The following rules apply to string concatenation:

  • The operands for + can be strings, packed strings (packed arrays of type Char), or characters. However, if one operand is of type WideChar, the other operand must be a long string (UnicodeString, AnsiString, or WideString).
  • The result of a + operation is compatible with any string type. However, if the operands are both short strings or characters, and their combined length is greater than 255, the result is truncated to the first 255 characters.

Pointer Operators

  • The relational operators <, >, <=, and >= can take operands of type PAnsiChar and PWideChar (see Relational operators). The following operators also take pointers as operands. For more information about pointers, see Pointers and Pointer Types (Delphi) in About Data Types (Delphi).

Character-pointer operators:

Operator Operation Operand Types Result Type Example

+

pointer addition

character pointer, integer

character pointer

P + I

pointer subtraction

character pointer, integer

character pointer, integer

P - Q

^

pointer dereference

pointer

base type of pointer

P^

=

equality

pointer

Boolean

P = Q

<>

inequality

pointer

Boolean

P <> Q

The ^ operator dereferences a pointer. Its operand can be a pointer of any type except the generic Pointer, which must be typecast before dereferencing.

P = Q is True just in case P and Q point to the same address; otherwise, P <> Q is True.

You can use the + and operators to increment and decrement the offset of a character pointer. You can also use to calculate the difference between the offsets of two character pointers. The following rules apply:

  • If I is an integer and P is a character pointer, then P + I adds I to the address given by P; that is, it returns a pointer to the address I characters after P. (The expression I + P is equivalent to P + I.) P - I subtracts I from the address given by P; that is, it returns a pointer to the address I characters before P. This is true for PAnsiChar pointers; for PWideChar pointers P + I adds I * SizeOf(WideChar) to P.
  • If P and Q are both character pointers, then P - Q computes the difference between the address given by P (the higher address) and the address given by Q (the lower address); that is, it returns an integer denoting the number of characters between P and Q.
P + Q is not defined.

Set Operators

The following operators take sets as operands.

Set Operators:

Operator Operation Operand Types Result Type Example

+

union

set

set

Set1 + Set2

difference

set

set

S - T

*

intersection

set

set

S * T

<=

subset

set

Boolean

Q <= MySet

>=

superset

set

Boolean

S1 >= S2

=

equality

set

Boolean

S2 = MySet

<>

inequality

set

Boolean

MySet <> S1

in

membership

ordinal, set

Boolean

A in Set1

The following rules apply to +, , and *:

  • An ordinal O is in X + Y if and only if O is in X or Y (or both). O is in X - Y if and only if O is in X but not in Y. O is in X * Y if and only if O is in both X and Y.
  • The result of a +, , or * operation is of the type set of A..B, where A is the smallest ordinal value in the result set and B is the largest.

The following rules apply to <=, >=, =, <>, and in:

  • X <= Y is True just in case every member of X is a member of Y; Z >= W is equivalent to W <= Z. U = V is True just in case U and V contain exactly the same members; otherwise, U <> V is True.
  • For an ordinal O and a set S, O in S is True just in case O is a member of S.

Relational Operators

Relational operators are used to compare two operands. The operators =, <>, <=, and >= also apply to sets.

Relational Operators:

Operator Operation Operand Types Result Type Example

=

equality

simple, class, class reference, interface, string, packed string

Boolean

I = Max

<>

inequality

simple, class, class reference, interface, string, packed string

Boolean

X <> Y

<

less-than

simple, string, packed string, PChar

Boolean

X < Y

>

greater-than

simple, string, packed string, PChar

Boolean

Len > 0

<=

less-than-or-equal-to

simple, string, packed string, PChar

Boolean

Cnt <= I

>=

greater-than-or-equal-to

simple, string, packed string, PChar

Boolean

I >= 1

For most simple types, comparison is straightforward. For example, I = J is True just in case I and J have the same value, and I <> J is True otherwise. The following rules apply to relational operators.

  • Operands must be of compatible types, except that a real and an integer can be compared.
  • Strings are compared according to the ordinal values that make up the characters that make up the string. Character types are treated as strings of length 1.
  • Two packed strings must have the same number of components to be compared. When a packed string with n components is compared to a string, the packed string is treated as a string of length n.
  • Use the operators <, >, <=, and >= to compare PAnsiChar (and PWideChar) operands only if the two pointers point within the same character array.
  • The operators = and <> can take operands of class and class-reference types. With operands of a class type, = and <> are evaluated according the rules that apply to pointers: C = D is True just in case C and D point to the same instance object, and C <> D is True otherwise. With operands of a class-reference type, C = D is True just in case C and D denote the same class, and C <> D is True otherwise. This does not compare the data stored in the classes. For more information about classes, see Classes and Objects (Delphi).

Class and Interface Operators

The operators as and is take classes and instance objects as operands; as operates on interfaces as well. For more information, see Classes and Objects (Delphi), Object Interfaces (Delphi) and Interface References (Delphi).

The relational operators = and <> also operate on classes.

The @ Operator

The @ operator returns the address of a variable, or of a function, procedure, or method; that is, @ constructs a pointer to its operand. For more information about pointers, see «Pointers and Pointer Types» in About Data Types (Delphi). The following rules apply to @.

  • If X is a variable, @X returns the address of X. (Special rules apply when X is a procedural variable; see «Procedural Types in Statements and Expressions» in About Data Types (Delphi).) The type of @X is Pointer if the default {$T} compiler directive is in effect. In the {$T+} state, @X is of type ^T, where T is the type of X (this distinction is important for assignment compatibility, see Assignment-compatibility).
  • If F is a routine (a function or procedure), @F returns F‘s entry point. The type of @F is always Pointer.
  • When @ is applied to a method defined in a class, the method identifier must be qualified with the class name. For example,
@TMyClass.DoSomething
points to the DoSomething method of TMyClass. For more information about classes and methods, see Classes and Objects (Delphi).

Note: When using the @ operator, it is not possible to take the address of an interface method, because the address is not known at compile time and cannot be extracted at run time.

Operator Precedence

In complex expressions, rules of precedence determine the order in which operations are performed.

Precedence of operators

Operators Precedence

@
not

first (highest)

*
/
div
mod
and
shl
shr
as

second

+

or
xor

third

=
<>
<
>
<=
>=
in
is

fourth (lowest)

An operator with higher precedence is evaluated before an operator with lower precedence, while operators of equal precedence associate to the left. Hence the expression:

X + Y * Z

multiplies Y times Z, then adds X to the result; * is performed first, because is has a higher precedence than +. But:

X - Y + Z

first subtracts Y from X, then adds Z to the result; and + have the same precedence, so the operation on the left is performed first.

You can use parentheses to override these precedence rules. An expression within parentheses is evaluated first, then treated as a single operand. For example:

(X + Y) * Z

multiplies Z times the sum of X and Y.

Parentheses are sometimes needed in situations where, at first glance, they seem not to be. For example, consider the expression:

X = Y or X = Z

The intended interpretation of this is obviously:

(X = Y) or (X = Z)

Without parentheses, however, the compiler follows operator precedence rules and reads it as:

(X = (Y or X)) = Z

which results in a compilation error unless Z is Boolean.

Parentheses often make code easier to write and to read, even when they are, strictly speaking, superfluous. Thus the first example could be written as:

X + (Y * Z)

Here the parentheses are unnecessary (to the compiler), but they spare both programmer and reader from having to think about operator precedence.

Function Calls

Because functions return a value, function calls are expressions. For example, if you have defined a function called Calc that takes two integer arguments and returns an integer, then the function call Calc(24,47) is an integer expression. If I and J are integer variables, then I + Calc(J,8) is also an integer expression. Examples of function calls include:

Sum(A, 63)
Maximum(147, J)
Sin(X + Y)
Eof(F)
Volume(Radius, Height)
GetValue
TSomeObject.SomeMethod(I,J);

For more information about functions, see Procedures and Functions (Delphi).

Set Constructors

A set constructor denotes a set-type value. For example:

[5, 6, 7, 8]

denotes the set whose members are 5, 6, 7, and 8. The set constructor:

[ 5..8 ]

could also denote the same set.

The syntax for a set constructor is:

[ item1, …, itemn ]

where each item is either an expression denoting an ordinal of the set’s base type or a pair of such expressions with two dots (..) in between. When an item has the form x..y, it is shorthand for all the ordinals in the range from x to y, including y; but if x is greater than y, then x..y, the set [x..y], denotes nothing and is the empty set. The set constructor [ ] denotes the empty set, while [x] denotes the set whose only member is the value of x.

Examples of set constructors:

[red, green, MyColor]
[1, 5, 10..K mod 12, 23]
['A'..'Z', 'a'..'z', Chr(Digit + 48)]

For more information about sets, see Structured Types (Delphi) in About Data Types (Delphi).

Indexes

Strings, arrays, array properties, and pointers to strings or arrays can be indexed. For example, if FileName is a string variable, the expression FileName[3] returns the third character in the string denoted by FileName, while FileName[I + 1] returns the character immediately after the one indexed by I. For information about strings, see Data Types, Variables and Constants. For information about arrays and array properties, see Arrays in Data Types, Variables, and Constants and «Array Properties» in Properties (Delphi) page.

Typecasts

It is sometimes useful to treat an expression as if it belonged to different type. A typecast allows you to do this by, in effect, temporarily changing an expression’s type. For example, Integer('A') casts the character A as an integer.

The syntax for a typecast is:

typeIdentifier(expression)

If the expression is a variable, the result is called a variable typecast; otherwise, the result is a value typecast. While their syntax is the same, different rules apply to the two kinds of typecast.

Value Typecasts

In a value typecast, the type identifier and the cast expression must both be ordinal or pointer types. Examples of value typecasts include:

Integer('A')
Char(48)
Boolean(0)
Color(2)
Longint(@Buffer)

The resulting value is obtained by converting the expression in parentheses. This may involve truncation or extension if the size of the specified type differs from that of the expression. The expression’s sign is always preserved.

The statement:

I := Integer('A');

assigns the value of Integer('A'), which is 65, to the variable I.

A value typecast cannot be followed by qualifiers and cannot appear on the left side of an assignment statement.

Variable Typecasts

You can cast any variable to any type, provided their sizes are the same and you do not mix integers with reals. (To convert numeric types, rely on standard functions like Int and Trunc.) Examples of variable typecasts include:

Char(I)
Boolean(Count)
TSomeDefinedType(MyVariable)

Variable typecasts can appear on either side of an assignment statement. Thus:

var MyChar: char;
  ...
  Shortint(MyChar) := 122;

assigns the character z (ASCII 122) to MyChar.

You can cast variables to a procedural type. For example, given the declarations:

type Func = function(X: Integer): Integer;
var
  F: Func;
  P: Pointer;
  N: Integer;

you can make the following assignments:

F := Func(P);     { Assign procedural value in P to F }
Func(P) := F;     { Assign procedural value in F to P }
@F := P;          { Assign pointer value in P to F }
P := @F;          { Assign pointer value in F to P }
N := F(N);        { Call function via F }
N := Func(P)(N);  { Call function via P }

Variable typecasts can also be followed by qualifiers, as illustrated in the following example:

type
  TByteRec = record
     Lo, Hi: Byte;
  end;
  TWordRec = record
     Low, High: Word;
  end;
  PByte = ^Byte;   

var
  B: Byte;
  W: Word;
  L: Longint;
  P: Pointer;

begin
  W := $1234;
  B := TByteRec(W).Lo;
  TByteRec(W).Hi := 0;
  L := $1234567;
  W := TWordRec(L).Low;
  B := TByteRec(TWordRec(L).Low).Hi;
  B := PByte(L)^;
end;

In this example, TByteRec is used to access the low- and high-order bytes of a word, and TWordRec to access the low- and high-order words of a long integer. You could call the predefined functions Lo and Hi for the same purpose, but a variable typecast has the advantage that it can be used on the left side of an assignment statement.

For information about typecasting pointers, see Pointers and Pointer Types (Delphi). For information about casting class and interface types, see «The as Operator» in Class References and Interface References (Delphi).

See Also

  • Fundamental Syntactic Elements (Delphi)
  • Declarations and Statements (Delphi)
  • Boolean short-circuit evaluation (Delphi compiler directive)
  • Operator Overloading (Delphi)
  • Pointers and Pointer Types (Delphi)

Знание синтаксиса языка лишь теоретически позволяет приступить к созданию программ. Дело в том, что важнейшей частью любого языка программирования, во многом определяющими удобство составления алгоритмов, являются его управляющие структуры — операторы, или инструкции.

Об управляющих структурах

В реальных программах выполнение операций не бывает строго последовательным: постоянно требуются различные переходы, ветвления, повторения и т.д. Так, если мы обратимся к блок-схеме даже такого простого алгоритма, который требуется для игры «угадай число» (см. рис. 1.1), то увидим там целых два ветвления, причем оба с переходом.

Собственно за переход, в классическом варианте, отвечает небезызвестная инструкция безусловного перехода goto, которая в Pascal используется совместно с метками, декларируемыми в заголовочной части программы при помощи ключевого слова label. Использование инструкции безусловного перехода восходит корнями к тем временам, когда создавались первые высокоуровневые языки программирования, в том числе и Pascal. Она досталась им в наследство от низкоуровневых языков типа Assembler, в которых описание программы создавалось в виде, удобным для машины. Но на сегодня такой подход уже не востребован и вышел из употребления, вместе с безусловным переходом и инструкцией goto.

СОВЕТ
За 10 лет, прошедших с того момента, как я последний раз использовал язык Basic, мне ни разу не довелось ни использовать инструкцию goto в собственных программах, ни встретить ее в миллионах строк просмотренного кода. Вывод: она просто не нужна в Object Pascal, так что старайтесь не использовать goto!

Инструкция goto относится к т.н. «простым операторам». К ним же относится пустой оператор, который вообще ничего не делает. Он может находиться в любом месте программы, где допускается, или требуется наличие какой-либо инструкции. Именно по второй причине, т.е. при необходимости использования инструкции в том или ином месте программы (по правилам языка), и скрывается вся ее ценность: поскольку пустая инструкция ничего не делает, то она является идеальной «заглушкой» в подобных ситуациях. Обозначается пустой оператор знаком «точка с запятой».

В тех же случаях, когда наоборот, по правилам языка можно использовать всего лишь одно выражение или оператор, а нужно несколько, используют составные операторы. Составной оператор представляет собой группу из произвольного числа любых инструкций, ограниченных ключевыми словами begin и end:

begin
<инструкция 1>;
<инструкция 2>;
...
<инструкция N>;
end;

Сколько бы ни входило инструкций в такой блок, для компилятора он воспринимается как единое целое и может располагаться в любом месте программы, где допускается наличие хотя бы одного оператора.

Составные операторы могут вкладываться один в другой, при этом глубина таких вложений в Object Pascal не ограничена.

Условный оператор if

Пожалуй, самой важной инструкцией для управления выполнением программы является условный оператор if. Именно он отвечает за ветвление, т.е. выполнение (или невыполнение) того или иного варианта кода в зависимости от условий. Оператор if используется совместно с ключевым словом then, а в том случае, когда предусмотрен альтернативный вариант выполнения — еще и с else. В целом синтаксис инструкции получается следующим:

if <условие> then <код> [else <альтернативный код>]

В качестве условия может быть использовано любое выражение, которое может быть приведено к булевскому значению, т.е. к false или true. Как правило, это бывают операции сравнения, например:

if a > 5 then b := 10;
if x <> 0 then y := 1 else y :=2;

В первом случае, если переменная a больше 5, то переменной b будет присвоено значение 10, если же a меньше или равно 5, то ничего выполнено не будет и управление будет передано следующему выражению. Во второй строке переменная x проверяется на ненулевое значение, и если x окажется числом, отличным от 0, то переменной y будет присвоено значение 1, в противном случае (если x равно 0) переменной y будет присвоено значение 2.

В тех случаях, когда для того или иного варианта кода требуется использовать более одного выражения, используют составной оператор:

if a > 5 then
begin
b := 10;
c := 20;
end;

Как мы уже знаем, в соответствии с правилами синтаксиса Object Pascal, все то, что помещено между ключевыми словами begin и end, равно как и сами эти слова, воспринимаются как 1 оператор. Обратите внимание, что в конце поставлен пустой оператор — точка с запятой. В данном случае по правилам синтаксиса он здесь не обязателен, однако будет хорошим тоном завершать им каждый составной оператор, чтобы выделить тем самым окончание той инструкции, в которой он был применен. В данном случае получается, что мы использовали 2 оператора — составной и пустой, однако нарушения синтаксиса тут нет — компилятор сочтет, что пустой оператор следует уже после условного. Но если бы мы использовали еще и блок else, это привело бы к ошибке синтаксиса, поскольку между then и else допустима лишь 1 инструкция. Поэтому в таком случае точку с запятой следует разместить уже после оператора, следующего за else:

if a > 5 then
begin
b := 10;
c := 20;
end
else
begin
b := 20;
c := 15;
end;

В тех случаях, когда требуется предусмотреть 3 или более вариантов исполнения, используют вложение операторов if друг в друга. Например, если требуется выполнить один вариант когда, когда некая переменная x меньше нуля, другой — если x равна 0, и третий — если x больше нуля, то синтаксис операторов может быть следующим:

if x < 0 then <вариант для x<0> else
if x = 0 then <вариант для x=0> else <вариант для x>0>;

В данном случае использован вложенный оператор if, который выполняется в случае, когда переменная x не меньше 0. Он проверяет, не является ли значением x число 0, и если нет, то, учитывая, что x явно не меньше, чем 0 (это условие к моменту выполнения вложенного оператора if уже проверено внешним, т.е. первым в данном выражении оператором if), значит значение x больше 0.

Оператор выбора case

Условный оператор удобен в тех случаях, когда необходимо проверить 1-2-3 варианта. При большем числе получается слишком громоздкая и неудобная для восприятия конструкция из множества вложенных инструкций. Скажем, если требуется проверить 5 значений переменной x, то получим такую конструкцию:

if x = 1 then ;
else if x = 2 then ;
else if x = 3 then ;
else if x = 4 then ;
else if x = 5 then ;

Очевидно, что код получается слишком громоздким, и малоэффективным. В таких случаях на помощь приходит семафор — оператор множественного выбора case. Он состоит из выражения, являющегося селектором, списка вариантов, представленного константами или значениями, и необязательной части else. Таким образом, формат оператора case таков:

case [выражение-селектор] of
  <значение 1>: <код для значения 1>;
  <значение 2>: <код для значения 2>;
  ...
  <значение N>: <код для значения N>;
  [else <код для непредусмотренных явно значений>;]
end

Единственным ограничением семафора, в сравнении с условным оператором, является то, что в качестве селектора могут выступать лишь данные порядкового типа, скажем, целым числом или же символом. Впрочем, для подавляющего числа случаев этого достаточно. Например, приведенный выше вариант кода с 4 вложенными условными операторами, при помощи case можно оформить так:

case x of
1: ;
2: ;
3: ;
4: ;
5: ;
end;

Здесь подразумевается, что типом переменной x является целое число, поскольку тип значений, коими в данном случае являются целые числа, должен соответствовать типу селектора.

Инструкция выбора выполняется следующим образом: вначале, при необходимости, вычисляется значение селектора, затем производится последовательный обход вариантов на предмет совпадения с селектором. В случае совпадения, выполняется инструкция, предусмотренная для этого варианта, после чего выполнение оператора выбора заканчивается. Если же ни один из перечисленных вариантов не совпал со значением селектора (для нашего случая — если x меньше 1 или больше 5), то либо оператор завершается без каких-либо действий, либо, при наличии блока else, выполняются заданные в нем инструкции.

В качестве констант выбора могут выступать не только единичные значения, но и их список, разделенный запятыми, или же диапазоны, определенные границами из 2 констант, разделенных двумя точками. В таком случае мы можем объединить логически связанные значения в группы, для которых следует выполнить один и тот же код. Например, таким образом можно получить название времени года в зависимости от порядкового номера месяца (листинг 4.1).

Листинг 4.1. Использование оператора case

var
month: integer;
season: string;
...
case month of
1,2,12: season := "зима";
3..5: season := "весна";
6..8: season := "лето";
9..11: season := "осень";
else season := "других не знаем!";
end;

В данном случае, если переменная month имеет значения 1, 2 или 12, то переменной season присваивается значение «зима», если же значение переменной month окажется в диапазоне от 3 до 5 (включительно), то season получит значение «весна», и т.д.

Оператор цикла for

Для написания практически любой программы, помимо операторов условия, требуются операторы цикла, и в Object Pascal, они, разумеется, есть. Прежде всего, это оператор цикла с параметром — for. Такой тип цикла обычно применяют в тех случаях, когда количество возможных повторов известно заранее. Он имеет 2 варианта написания: один — для цикла с приращением, и другой — для цикла с уменьшением:

for <параметр> := <выражение 1> to <выражение 2> do <тело цикла>;
for <параметр> := <выражение 1> downto <выражение 2> do <тело цикла>;

В первом случае (с использованием цикла for-to) при каждом проходе цикла, называемом итерацией, значение параметра увеличивается на 1, а во втором (for-downto) — уменьшается на 1. При этом в качестве начального значения используется «выражение 1», а в качестве конечного — «выражение 2». Разумеется, если для цикла to значение первого выражения изначально будет больше значения второго, или наоборот, меньше (для цикла downto), то цикл не будет выполнен ни разу.
Практическое применение циклов крайне разнообразно. Если привести наиболее общий пример из программирования, то цикл — идеальный способ заполнения массива. Например, если требуется организовать цикл для заполнения массива из 10 числовых значений последовательно возрастающими числами, то можно записать:

for i := 0 to 9 do MyArray[i]=i;

В данном случае элементам массива MyArray последовательно назначаются значения от 0 до 9.

ПРИМЕЧАНИЕ
Сами массивы будут рассмотрены несколько позже, в главе, посвященной структурным типам данных.

Теперь рассмотрим цикл for с отрицательным приращением на примере вычисления математического факториала (последовательное произведение всех целых чисел от 1 до самого числа). Для этого нам понадобится следующий цикл:

var num, rez: integer;
...
rez := 1;
for num := num downto 1 do rez := rez * num;

Здесь нам потребовалась небольшая подготовительная работа: определена переменная rez, в которой будет храниться вычисляемое значение, и ей присвоено значение 1. В качестве числа, для которого вычисляется факториал, выступает переменная num, она же используется для самого цикла в качестве счетчика. Поскольку нам надо будет прекратить выполнение цикла, после того, как счетчик (num) достигнет значения 1, то именно это значение и указано в качестве конечного условия.

В итоге, если переменной num присвоить значение 5, то после прохождения цикла переменная rez получит значение 120. Хотя в результате работы такого цикла получится выполнение как бы наоборот (т.е. не вместо 1*2*3*4*5, на самом деле выполняется 5*4*3*2*1), это никак не помешает получить верный результат.

Наконец, в качестве тела цикла, как и в случае с уже рассмотренными операторами, может использоваться составной оператор. Кроме того, циклы могут быть вложены один в другой, при этом важно лишь следить за тем, где заканчивается вложенный цикл и начинается внешний. Для этого полезно следить за правильным оформлением программы, в частности, использовать отступы, в качестве которых можно использовать либо знак табуляции, либо пробелы, в последнем случае их желательно ставить не менее 2 (листинг 4.2).

Листинг 4.2. Вложенные циклы и форматирование кода

for x := 5 to 10 do begin
z := x;
for y := 10 to 20 do begin
z := z + x * y;
writeln(z);
end; // конец вложенного цикла
writeln(x);
end; // конец внешнего цикла

При использовании циклов с параметром важно помнить, что изменение значения параметра в теле цикла недопустимо.

Операторы циклов while и repeat

Помимо классического цикла с параметром, в Object Pascal предусмотрено еще 2 вида циклов — с предусловием и с постусловием. В качестве цикла с предусловием выступает оператор while. Он имеет следующий синтаксис:

while <Условие> do <тело цикла>

Этот цикл будет выполняться до тех пор, пока верно выражение, указанное в качестве условия. Соответственно, если значение выражение будет изначально ложным, то цикл не будет выполнен ни разу, например:

while false do ;
В то же время, при помощи оператора while удобно делать бесконечный цикл. В бесконечных циклах весь контроль возлагается на операторы, помещаемые в тело цикла:
while true do begin
<инструкции>
end;

ПРИМЕЧАНИЕ
Очевидно, что цикл с условием, которое изначально истинно и никак не изменяется, будет выполняться вечно. Таким образом, в теле цикла следует предусмотреть возможность его прерывания иным способом.

В отличие от while, цикл с постусловием, задаваемый при помощи оператора repeat, всегда выполняется хотя бы 1 раз, поскольку проверку на соответствие условию он проходит после того, как будет выполнено его тело:

repeat <тело цикла> until <условие>

Важно так же отметить, что в цикле с постусловием ключевые слова repeat и until образуют как бы составной оператор. Иначе говоря, если в цикле while при необходимости использовать более одной инструкции следует использовать составной оператор, то для цикла repeat этого не требуется:

repeat
x := x + 1;
y := x * 2;
until y < 1000;

При использовании циклов while и repeat важно помнить, что хотя бы одна из инструкций, выполняемых в теле цикла, должна влиять на значение его условия, в противном случае вы рискуете получить зацикливание.

Операторы break и continue

Мы уже поднимали вопрос бесконечного цикла и возможного зацикливания программы, чреватого ее зависанием. Чтобы иметь возможность обработки подобных ситуаций, а так же сделать сами циклы более гибкими, используют специальные операторы — break и continue. Оператор break позволяет завершить цикл досрочно, а оператор continue — выполнить только часть операторов в теле цикла, перейдя к его следующей итерации.

Так, досрочный выход из цикла, определяемый при помощи оператора break, происходит в месте вызова этого оператора, и управление будет передано первому оператору, находящемуся после цикла. Например, в уже рассмотренном нами бесконечном цикле, можно задать условие его прерывания таким образом:

while true do begin
a := b * c;
if a > 1000 then break;
b := b + 1; // в случае если a > 1000, эта строка выполнена не будет
end;

Еще одним полезным применением досрочного выхода является его использования в качестве дополнительного параметра. Например, если нам нужен цикл, который должен прерваться по 1 из 2 условий, то для второй проверки мы можем использовать условный оператор if совместно с break:
repeat
i := i + 1;
if i > 100 then break;
y := y - 1;
until y < 50;

Здесь мы определили цикл, который будет завершен либо после того, как значение переменной y достигнет 50 (что задано в самом условии цикла), либо если значение переменно x превысит указанное в условии if значение 100 — здесь как раз будет задействован оператор break.

Иногда бывает необходимо перейти к следующему шагу цикла досрочно, пропустив часть операторов. Для этих целей используют оператор continue. В отличие от break, этот оператор не завершает цикл, а заставляет программу досрочно перейти к новой проверке условия цикла. Рассмотрим эту ситуацию на примере. Допустим, что нам надо получить список чисел, на которые число 100 делится без остатка:

for i := 1 to 100 do begin
if 100 mod i <> 0 then continue;
writeln(i);
end;

Для этого мы определили цикл, в котором при каждой итерации делитель, в качестве которого выступает счетчик цикла, увеличивается на единицу. В самом теле цикла использован условный оператор, в котором условием выступает выражение, в котором производится операция «остаток от деления» и ее результат сравнивается с 0. Таким образом, если дано условие истинно (т.е. если остаток равен нулю), то интерпретатор переходит к следующему оператору в теле цикла, который выводит нужное нам число, в противном случае выполняется оператор continue и начинается новый шаг цикла.

Операторы в действии

Таким образом, мы рассмотрели практически все операторы языка Object Pascal, включая 3 типа стандартных циклов. Еще один цикл — for-in, появившийся в Delphi 2005, является частным случаем цикла for-to и позволяет без лишних усилий создавать циклы для данных порядкового типа. Тем не менее, останавливаться на возможностях языка, появившихся после Delphi 7, мы здесь не будем. Рассмотрим лучше практическое применение изученных операторов для реализации алгоритмов, для чего вновь обратимся к игре «угадай число».

Игра выполняется до тех пор, пока пользователь не угадает число из заданного диапазона (скажем, от 0 до 100). При этом, если предложенный пользователем ответ больше или меньше загаданного программой числа, то программа должна выводить соответствующую подсказку. Соответственно, для реализации этой программы нам понадобятся следующие операторы:

  • Цикл while, выполняющийся до тех пор, пока число не угадано
  • Два условных оператора if, которые будет проверять, не является ли ответ большим или меньшим, и выводить соответствующую подсказку

Помимо них, нам потребуются несколько стандартных функций Object Pascal — для вывода информационных сообщений, для ввода пользователем значений, а так же для «загадывания» числа. Ввод и вывод в консольных программах осуществляется при помощи функций read и write, соответственно. А для генерации псевдослучайных чисел используют процедуру randomize и функцию random.

ПРИМЕЧАНИЕ
Несколько позже мы подробно ознакомимся с тем, что такое процедуры и функции, и чем они различаются. Пока же достаточно просто принять к сведению, что перечисленные здесь функции будут делать в данном случае.

Итак, для начала запустите Delphi и создайте новое приложение командной строки (File New Other New/Console Application). После того, как Delphi создаст новый проект, сразу сохраните его под каким-либо осмысленным именем, попутно выбрав для него подходящее место на диске. Назовем эту программу «Ugadaika» и помести ее в одноименный каталог на диске C:.

Теперь можно приступать к написанию кода. Поскольку программа должна начинаться с деклараций переменных, то с них и начнем. Нам понадобятся 2 переменных: одна — для хранения загаданного числа, и вторая — для получения ответа пользователя. Назовем их a и b и поместим объявление перед ключевым словом begin. Таким образом, в самом начале работы программа примет такой вид, как показано на примере листинга 4.3.

Листинг 4.3. Программа «угадай-ка» в самом начале разработки

program ugadaika;
{$APPTYPE CONSOLE}
uses
SysUtils;
var
a,b: integer;
begin

end.

Теперь можно приступать к написанию основного кода, который, как мы знаем, должен располагаться между begin и end. Начинаться он должен с загадывания числа. Для этого поместим в начало программы (начиная со строки, следующей за begin), следующий код:

randomize; // инициализация генератора псевдослучайных чисел
a := random(100)+1; // присваивание переменной a значения от 1 до 100

Далее следует подготовиться к циклу отгадывания, для чего надо, во-первых, установить переменную b в заведомо неподходящее значение (скажем, в 0), а во-вторых — вывести пользователю сообщение, в котором пояснить, чего ему надо делать:

b:=0;
write(Input a number between 1 to 100 and hit Enter);

Здесь мы использовали английские символы по той причине, что кодовые номера символов кириллицы для DOS и Windows различаются. Соответственно, чтобы опустить здесь процедуры перекодировки символов, мы пошли по пути наименьшего сопротивления и ограничились латиницей.

Следующим этапом будет создание самого цикла. Мы уже определили условие его выполнения — до тех пор, пока число не отгадано, т.е. пока a не равно b. Соответственно, выглядеть он будет так:

while (a<>b) do

Кроме того, поскольку в цикле будет явно больше 1 оператора, сразу определим составной оператор, добавив begin и end. В результате общий вид программы на текущем этапе получится таким, как показано на листинге 4.4.

Листинг 4.4. «Угадай-ка» с основными блоками

program ugadaika;
{$APPTYPE CONSOLE}
uses
SysUtils;
var
a,b: integer;
begin
randomize;
a := random(100)+1;
b := 0;
write(Input a number between 1 to 100 and hit Enter);
while (a<>b) do begin

end;
end.

Теперь займемся телом цикла. Для начала нам надо предоставить пользователю предложить свой вариант ответа. Однако не будем забывать, что пользователь — тоже человек, и не помешало бы вывести подсказку перед точкой ввода числа, хотя бы самую простую, состоящую из знака вопроса с двоеточием:

write(#13+#10+?:);

Использованные здесь символы #13 (возврат каретки) и #10 (новая строка) переносят точку ввода на одну строку вниз для лучшего эстетического восприятия. Теперь дадим пользователю возможность ввести свой вариант ответа, воспользовавшись функцией read:

read(b); // введенное число будет помещено в переменную b

Теперь остается написать условные операторы, проверяющие введенное число на соответствие загаданному и выдающими нужную подсказку. Начнем со случая, когда введенное пользователем число больше загаданного:

if (b>a) then write(Too much!);

Теперь составим условие для противоположного случая:

if (b<a) then write(Too small!);

Вот, в общем-то, и все! Остается после завершения цикла предусмотреть вывод сообщения о том, что число отгадано, для чего вновь воспользуемся функцией write, а заодно — функцией sleep, чтобы предотвратить преждевременное закрытие окна:

write(Cool! You win!);
sleep(2000); // пауза в 2 секунды

В итоге мы получим вполне работоспособную программу, полный исходный код которой приведен в листинге 4.5, а на CD он находится в Demo\Part1\Ugadaika.

Листинг 4.5. Полный исходный код игры

program ugadaika;
{$APPTYPE CONSOLE}
uses
SysUtils;
var
a,b: integer;
begin
randomize;
a := random(100)+1;
b := 0;
write(Input a number between 1 to 100 and hit Enter);
while (a<gt;b) do begin
write(#13+#10+'?:');
read(b);
if (b>a) then write(Too much!);
if (b<a) then write(Too small!);
end;
write(Cool! You win!);
sleep(2000);
end.

« Введение в Object Pascal
|
Структурные типы данных »

Техподдержка / Связаться с нами
Copyright © 1999-2020 SNK. Все права защищены.
При использовании материалов с сайта ссылка на источник обязательна.
Рейтинг@Mail.ru

Языки программирования

После знакомства с основами объявления переменных и констант и построения простейших структур нам предстоит перейти на очередной уровень освоения языка — научиться использовать в программах операторы и выражения Delphi.

В терминах программирования под выражением понимается логически законченный фрагмент исходного кода программы, предоставляющий способ получения (вычисления) некоторого значения. Простейшим примером выражения может стать строка кода X:=Y+Z, возвращающая результат суммирования двух значений. Предложенное выражение содержит три операнда (X, Y и Z) и два оператора: := и +.

Перечень операторов входящих в состав языка Delphi весьма обширен, при классификации операторов можно выделить следующие группы:

оператор присваивания;

арифметические операторы;

оператор конкатенации строк;

логические операторы;

операторы поразрядного сдвига;

операторы отношения;

операторы множеств;

строковые операторы;

составной оператор;

условные операторы.

Оператор присваивания

Едва ли не самый популярный среди всех операторов Delphi — оператор присваивания нам уже хорошо знаком. Комбинация символов «:=» уже неоднократно встречалась на предыдущих страницах книги, с ее помощью мы передавали значения в переменные. Например,

X:=10; //присвоить переменной X значение 10

Благодаря оператору := в переменной X окажется новое значение.

Арифметические операторы

Как и следует из названия, арифметические операторы необходимы для осуществления математических действий с целыми и вещественными типами данных. Помимо известных еще из курса начальной школы операторов сложения, вычитания, умножения и деления, Delphi обладает еще двумя операторами целочисленного деления (табл. 1.1).

Таблица 1.1. Арифметические операторы Delphi

Операто

Операция

Входные

Результат

Пример

Результ

р

значения

операции

ат

+

Сложение

integer,

integer,

X:=3+4;

7

double

double

Вычитание

integer,

integer,

X:=10-3.1;

8.9

double

double

*

Умножение

integer,

integer,

X:=2*3.2;

4.4;

double

double

/

Деление

integer,

double

X:=5/2;

3.5;

double

1

СКФУ Кафедра компьютерной безопасности

Языки программирования

div

Целочисленно

integer

integer

X:=5 div 2;

2

е деление

mod

Остаток от

integer

integer

X:=5 mod 2;

1

деления

При объявлении участвующих в расчетах переменных следует учитывать тип данных, возвращаемый в результате выполнения того или иного оператора. Допустим, нам следует разделить число 4 на 2 (листинг 1.1).

Листинг 1.1. Операция деления возвращает вещественное число

var {X:integer; — неподходящий тип данных}

X:extended;{- правильно} begin

X:=4/2; //результат должен быть передан в переменную вещественного типа

WriteLn(X); end.

Даже ученик начальной школы знает, что 4/2=2, другими словами в результате деления мы получим целое число. Однако Delphi обязательно забракует код, если мы попытаемся поместить результат операции деления в целочисленную переменную, и уведомит об этом программиста сообщением, о несовместимости типов.

Примечание

Операторы + и могут применяться не только для сложения и вычитания, но и для определения знака значения. Например: X:=-5.

Оператор конкатенации строк

Оператор конкатенации строк позволяет объединять две текстовые строки в одну. Для простоты запоминания еще со времен языка Pascal в качестве объединяющего оператора используется тот же самый символ, что и для сложения двух числовых величин — символ плюса + (листинг 1.2).

Листинг 1.2. Конкатенация строк

const S1=’Hello’;

var S:String=’World’;

CH:Char=’!’; begin

S:=S1+’, ‘+S+CH;

WriteLn(S); //’Hello, World!’

ReadLn; end.

Обратите внимание, что оператор конкатенации может быть применен не только для типа данных String, но и к символьному типу Char.

Логические операторы

В табл. 1.2 представлены четыре логических (булевых) оператора, осуществляющих операции логического отрицания, логического «И», логического «ИЛИ» и исключающего «ИЛИ». В результате выполнения любой из логических операций мы можем ожидать только одно значение из двух возможных: истина (true) или ложь (false).

2

СКФУ Кафедра компьютерной безопасности

Языки программирования

Таблица 1.2. Логические операторы Delphi

Оператор

Операция

Пример

Результат

NOT

Логическое отрицание

var x: boolean = NOT true;

false

x:= NOT false;

true

AND

Логическое умножение

x:=true AND true;

true

(конъюнкция, логическое «И»)

x:=true and false;

false

для двух выражений

x:=false AND true;

false

x:=false AND false;

false

OR

Выполняет операцию

x:=true OR true;

true

логического «ИЛИ» (сложения)

x:=true OR false;

false

для двух выражений

x:=false OR true;

true

x:=false OR false;

false

XOR

Выполняет операцию

x:=true XOR true;

false

исключающего «ИЛИ» для двух

x:=true XOR false;

true

выражений

x:=false XOR true;

true

x:=false XOR false;

false

Логические операции разрешено проводить с целыми числами. Если вы не забыли порядок перевода чисел из десятичной системы счисления в двоичную и наоборот, то вам наверняка покажется интересным листинг 1.3.

Листинг 1.3. Логические операции с целыми числами

var X,Y,Z : byte; begin

{******* логическое умножение *******}

X:=5; //в бинарном виде 0101

Y:=3; //в бинарном виде 0011

Z:=X AND Y; // 0101 AND 0011 = 0001 {десятичное 1} WriteLn(X,’ AND ‘,Y,’=’,Z);

{******* логическое сложение *******}

X:=1; //в бинарном виде 0001

Y:=2; //в бинарном виде 0010

Z:=X OR Y; // 0001 OR 0010 = 0011 {десятичное 3} WriteLn(X,’ OR ‘,Y,’=’,Z);

{******* исключение или *******}

X:=5;

//в бинарном виде 0101

Y:=3;

//в бинарном виде 0011

Z:=X XOR Y; // 0101 XOR 0011 = 0110 {десятичное 6}

WriteLn(X,’ XOR ‘,Y,’=’,Z);

{******* отрицание *******}

00000001

X:=1;

//в бинарном виде

{десятичное 254}

Z:=NOT X; // NOT 00000001 =

11111110

WriteLn(‘NOT ‘,X,’=’,Z);

ReadLn; end.

3

СКФУ Кафедра компьютерной безопасности

Языки программирования

Операторы поразрядного сдвига

С отдельными битами значения способны работать операторы поразрядного сдвига SHL и SHR. Первый из операторов осуществляет сдвиг влево (после оператора указывается количество разрядов сдвига), второй — поразрядный сдвиг вправо.

В листинге 1.4 представлен фрагмент кода, демонстрирующий возможности операторов сдвига.

Листинг 1.4. Поразрядный сдвиг влево

var i:integer=1; begin

while true do begin

i:=i SHL 1; //сдвиг влево на 1 разряд

WriteLn(i);

if i>=1024 then break; //выход из цикла end;

readln; end.

Если вы запустите программу на выполнение, то получите весьма нетривиальный результат. Оператор сдвига обрабатывает исходное значение, равное 1 (в двоичном представлении 00000001). Каждый шаг цикла сдвигает единичку на одну позицию влево. В итоге на экране компьютера отобразится следующий столбик значений:

2 {что в двоичном представлении соответствует 00000010}

4

{00000100}

8

{00001000}

16

{00010000}

32

{00100000}

64

{01000000}

и т. д.

По сути, мы с вами реализовали программу, позволяющую возводить значение 2 в заданную степень.

Можете провести эксперимент, приводящий к прямо противоположному результату. Для этого проинициализируйте переменную i значением 2 в степени N (например, 210 = 1024), замените оператор SHL на SHR и перепишите условие выхода из цикла: if i<1 then break.

Операторы отношения

Операторы отношения (неравенства) обычно применяются для сравнения двух числовых значений, в результате сравнения возвращаются логические значения true/false (табл. 1.3). Операторы отношения — желанные гости в условных операторах.

Таблица 1.3. Операторы отношения

Оператор

Операция

Пример

Результат

=

Сравнение

10=5

false

<>

Неравенство

10<>5

true

>

Больше чем

10>5

true

<

Меньше чем

10<5

false

>=

Больше или равно

10>=5

true

<=

Меньше или равно

10<=5

false

4

СКФУ Кафедра компьютерной безопасности

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]

  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #
  • #

Понравилась статья? Поделить с друзьями:
  • Виме абс инструкция по применению в ветеринарии
  • Фп 22 газоанализатор течеискатель инструкция по эксплуатации на русском
  • Либхерр русланд руководство
  • Acer mg43m руководство
  • Целекоксиб таблетки 100 мг цена инструкция по применению