(тип элементов может быть и любым другим), и надо скопировать тысячу элементов, начиная с первого на сто индексов выше (т.е. как бы сдвинуть значения с первого на сто первый индекс). Обычное, но необдуманное решение проблемы — цикл FOR:
for i:=1 to 1000 do A[ i+100] := A[ i ];
который лишь испортит данные массива A. Это произойдет из-за перекрытия исходного и конечного отрезков значений (рис. 14.6).
Рис. 14.6
Здесь элементы, начиная со 101-го, будут замещены раньше, чем скопированы! Корректный цикл FOR должен быть убывающим:
for i:=1000 downto 1 do A[ i+100 ] := А[ i ];
Чтобы избежать всех этих сложностей, рекомендуем пользоваться процедурой Move. Рассматриваемая задача решается так:
Move( А[ 1 ], А[ 101 ], 1000 * SizeOf( Real ) );
При этом проблема перекрытия снимается автоматически самой процедурой Move (она выбирает направление копирования исходя из заданных параметров). Параметры A[1] и A[101] в вызове имеют смысл не значений 1-го и 101-го элементов массива A, а адресов этих элементов в памяти.
Если же понадобится вернуть 1000 элементов «назад» в первую позицию массива, вызов будет таким:
Move( А[ 101 ], А[ 1 ], 1000 * SizeOf( Real ) );
и проблема перекрытия вновь будет решена автоматически.
Помните, что никаких проверок на корректность значений параметров в Move не происходит, и если значение длины блока больше, чем может вместить переменная — адрес назначения (второй параметр Move), то будет заполнена часть памяти, отведенная под другие
- 304 -
данные, что весьма неприятно. Советуем использовать функцию SizeOf для вычисления длин блока. Выражения типа 1000*SizeOf (Real) состоят из констант и могут быть вычислены еще во время компиляции программы, так что длинные арифметические последовательности в вызове Move ничуть не замедляют работу программ.
14.6. Функции обработки машинных слов Lo, Hi и Swap
Среди прочих специальных средств низкого уровня Турбо Паскаль предоставляет несколько удобных функций для работы над отдельными байтами машинных слов. Некоторые системные переменные и функции Турбо Паскаля возвращают два однобайтовых значения, объединенных в тип Word. Для их «распаковки» как раз подходят описываемые здесь функции. Их три (табл. 14.1).
Таблица 14.1
Функция : Тип | Возвращаемое значение |
Hi(X) : Тип-X | Старший байт аргумента X |
Lo(X) : Тип-X | Младший байт аргумента X |
Swap(X) : Тип-X | Число с переставленными старшим и младшим байтами |
Аргумент X имеет тип Word или Integer ( 2-байтовый целый), и возвращаемое значение имеет тот же тип, что и X.
Любое число типа Word раскладывается на два слагаемых из значений своих байтов по правилу (для типа Integer эта формула не подходит)
X := Hi(X) * 256 + Lo(X);
Функция Swap возвращает число типа Word или Integer в зависимости от типа аргумента X, в котором старший и младший байты поменялись местами. Следует быть осторожным при использовании функции с аргументом типа LongInt. В версии 5.5 Турбо Паскаля компилятор «ничего не имеет против» таких вызовов, но возвращаемое значение при счете усекается до типа Word, меняя его иной раз до неузнаваемости.
14.7. Вставки машинного кода в программе
14.7.1. Оператор inline
Несмотря на то, что средствами Турбо Паскаля можно сделать практически все, не всегда полученный результат будет максимально
- 305 -
эффективным по быстродействию, если сравнивать с грамотно написанной программой на ассемблере. Другой вопрос, за сколько дней и ночей будет написан и отлажен ассемблерный текст, а за сколько — программы на Турбо Паскале. Все это верно и для других языков. В последних версиях языка Си можно просто писать вставки на ассемблере посреди текста Си-программ. Довольно похожим способом решена проблема включения машинных команд в текст программ на Турбо Паскале. Правда, в текущих версиях языка для того надо употреблять не мнемонические команды языка ассемблера, а их аналоги в машинном коде. Оператор вставки машинного кода записывается с помощью зарезервированного слова inline и списка кодов, разделенных косой чертой:
inline ( Код1/ Код2/ Код3/ .../ КодN );