 |
должны сделать это сами с помощью операторов присваивания или
описав такие переменные в виде типизованных констант. Рассмотрим
следующую программу:
program Test;
var
I,J,Count : integer;
begin
for I := 1 to Count do begin
J := I*J;
Writeln(I:2,' ',J:4)
end
end
Здесь Count будет иметь какое-то случайное значение, содер-
жащееся в занимаемой этой переменной ячейке памяти, поэтому вы не
сможете определить, сколько раз будет выполнен данный цикл. Кроме
того, переменные, описанные внутри процедуры или функции, будут
создаваться каждый раз при входе в эту подпрограмму и уничтожать-
ся при выходе из нее. Поэтому нельзя полагать, что эти переменные
в промежутке между вызовами подпрограммы сохраняют свое значение.
Неправильная работа с указателями
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
Этот общий тип ошибок встречается при работе с указателями.
Во-первых, как уже упоминалось ранее, не следует использовать их
до того, как им будет присвоено значение (nil (пустое) или какое-
либо другое). Как и все другие переменные или структуры данных,
указатель не инициализируется автоматически при его описании. Ему
нужно явным образом присвоить начальное значение (передав его в
качестве параметра процедуре New или возможно быстрее присвоив
ему значение nil).
Во-вторых, не ссылайтесь на пустой указатель, то есть не пы-
тайтесь обратиться к данным или структуре, на которые он указыва-
ет, если указатель имеет значение nil. Например, предположим, что
у вас имеется линейный связанный список записей, и вы хотите вы-
полнить в нем поиск записи с заданным значением. Ваша программа
TDeb 3.0 #2-3 = 133 =
может выглядеть следующим образом:
function FindNode(Head : NodePtr, Val : integer);
var
Temp : NodePtr;
begin
Temp := Head;
while (Temp^.Key <> Val) and (Tamp <> nil) do
Temp := Temp^.Next
FindNode := Temp
end { FindNode }
Если Val не равно полю Key в каком-либо из узлов связанного
списка, то эта программа, когда Temp имеет значение nil, будет
пытаться вычислить Temp^.Key, что приведет к непредсказуемому по-
ведению. Каково же здесь решение? Нужно записать выражение следу-
ющим образом:
while (Temp <> nil) and (Temp^.Key <> Val)
и разрешить вычисление булевских выражений по короткой схеме (с
помощью директивы Турбо Паскаля {$B-} или команды
OptionsіCompilerіBoolean (ПараметрыіКомпиляторіБулевские выраже-
ния)). Таким образом, если Temp не равно nil, второе условие вы-
числяться не будет.
Наконец, не следует предполагать, что указатель устанавлива-
ется в значение nil только потому, что вы передаете его процедуре
Dispose или FreeMem. Указатель будет иметь при этом свое исходное
значение, однако память, на которую он указывает, будет теперь
освобождена, и может использоваться для другой динамической пере-
менной После освобождения структуры данных указатель нужно явным
образом установить в значение nil.
Неправильное использование области действия
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
Паскаль позволяет вам использовать большой уровень вложен-
ности процедур и функций, и в каждой их этих процедур и функций
могут содержаться ее собственные описания. Рассмотрим следующую
программу:
program Confused;
var
A,B :integer;
procedure Swap(var A,B : integer);
var
T : integer;
begin
Writeln('2: A,B,T = ',A:3,B:3,' ',T);
T := A;
A := B;
TDeb 3.0 #2-3 = 134 =
B := T;
Writeln('3: A,B,T =',A:3,B:3,' ',T);
end { Swap }
begin { тело основной программы Confused }
A:= 10; B := 20; T := 3-;
Writeln('1: A,B,T = ',A:3,B:3,' ',T);
Swap(B,A);
Writeln('4: A,B,T = ',A:3,B:3,' ',T);
end. { Confused }
Выводимая программой информация будет выглядеть примерно
следующим образом:
1: A,B,T = 10 20 30
2: A,B,T = 20 10 22161
3: A,B,T = 10 20 20
4: A,B,T = 20 10 30
Все это вызвано тем, что у вас имеется две версии переменных
A, B и T. В теле основной программы используются глобальные вер-
сии, в процедуре Swap - локальные версии (ее формальные параметры
A и B и локальная переменная T). И что еще более запутало ситуа-
цию, мы обратились с вызовом Swap(B,A), что означает, что фор-
мальный параметр A является на самом деле глобальной переменной B
и наоборот. И, конечно, нет никакой связи между локальной и гло-
бальной версией переменной T.
Настоящей ошибки здесь нет, но проблемы могут возникнуть,
когда вы будете считать, что модифицируете что-то, а на самом де-
ле это не так. Например, переменная T в теле основной программы
не изменяется, хотя вы можете предполагать, что это не так. Этот
результат, обратный описанным ранее "скрытым эффектам".
Если бы вы использовали следующее описание записи, все стало
бы еще более запутанным:
type
RecType = record
A,B : integer;
end;
var
A,B : integer;
Rec : RecType;
В операторе with ссылка на A или B привела бы к ссылке на
fields, а не к ссылке на variables.
Неправильное использование точки с запятой
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
Как и язык Си, Паскаль допускает использование "пустого"
TDeb 3.0 #2-3 = 135 =
оператора (оператора, состоящего только из точки с запятой). Раз-
мещенная в неверном месте точка с запятой может вызвать различные
проблемы. Рассмотрим следующую программу:
program Test;
var
I,J : integer;
begin
for I := 1 to 20 do;
begin
J := I*I;
Writeln(I:2,' ',J:4)
end;
Writeln('Выполнено!')
end.
Выводом этой программы будет не список из первых 20 целых
чисел и их квадратов, а просто:
20 400
Выполнено!
Это вызвано тем, что оператор for I := 1 to 20 заканчивается
точкой с запятой. При этом 20 раз будет выполнен пустой оператор.
После этого выполняется оператор в блоке begin...end и, наконец,
оператор Writeln. Чтобы исправить эту ошибку, нужно просто устра-
нить точку с запятой за ключевым словом do.
Функция возвращает неопределенное значение
ДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДДД
Когда вы пишете функцию, нужно убедиться, что перед тем, как
функция возвращает управление, ее имени присваивается некоторое
значение. Рассмотрим следующий пример кода:
const
NLMax = 100;
type
NumList = array[1...NLMax] of integer;
...
function FindMax(List : Numlist; Count : integer) : integer;
var
I,MAX : integer;
begin
Max := List[1];
for I := 2 to Count do
|