Решение практических задач на Паскале. Выпуск 7


Возведение матрицы в целочисленную степень. 2

Что такое безрамерный массив?

В предыдущем выпуске рассылки был описан способ возведении матрицы в целочисленную степень. Недостаток "того" метода заключался в том, что для изменения ранга матрицы необходимо заново компилировать программу. Это, ессно, неудобно. И я обещал предложить другой способ.

Но способ опишу нестандартный. Это не пресловутый список. Идея простая: отключается проверка принадлежности диапазону с помощью указания компилятору {$R-}. Объявляется массив минимальной размерности, например, ar = array[1..1, 1..1] of Real;.

Благодаря такому определению Паскаль "знает", что ar - это массив, у него есть элементы, к которым можно обращаться по их индексу. А в результате того, что отключена проверка принадлежности диапазону, он и не "догадывается", что мы обращаемся к элементам с индексами больше объявленных.

Следовательно, "делай, с ним что хошь". Нужно только разместить этот массив в динамической памяти и "пометить" ее как занятую. Для обычного блока данных программы этого сделать нельзя. Помечать нужно, чтобы нечаянно не переписать данные

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

var
   k: Integer;
   ar: array[1..1, 1..1] of Real;
   i: Integer;
BEGIN
   WriteLn;
   ar[1,1]:=0.5;
   WriteLn('Это правильно, как и ввели: ', ar[1,1]:5:1);
   i:=2; {!!!} k:=i;
 {$R-}     {От сех пор не проверять принадлежность диапазону}
   ar[1, i]:=0.11; {Константу использовать нельзя! Используем переменную i}
   WriteLn('И это правильно? ', ar[1, k]:5:2,' Не так ли?');
   WriteLn('Тогда это как понимать? i стала равным ',i);
   WriteLn('ВСЕ ПРАВИЛЬНО (в смысле, неправильно)!! Когда писали в элемент ar[1, 2], то'+
     #13#10#9'переписали содержимое переменной i!');
   WriteLn('В оперативной памяти компьютера она находится за массивом');
END.

Надеюсь, помните: не следует буквально понимать фразу "набирать код". Выделите его (код), нажмите Ctrl+C для того, чтобы скопировать текст в карман Windows (Clipboard). Переведите редактор Паскаля в оконный вид (Alt+ENTER). Создайте новый фа йл. Зайдите в системное меню окна (в самом верхнем левом углу окна, где написано Borland Pascal 7.0). Выберите пункт меню "Изменить - Вставить". Нажмите F2 для сохранения текста программы

Итак, все идем в "кучу" (так называют динамическую память)

В нашем случае для размещения массива в куче следует использовать процедуру GetMem. Есть еще New, но она использует информацию о структуре размещаемого в памяти элемента. В частности, о его длине. А нам нужно захватить памяти больше, чем положено

Однако, реализация сложнее, чем просто вызвать GetMem, а потом обращаться к "запредельным" элементам массива. На это обычно не обращают внимания, так как используют только отдельные элементы массива, а НЕ ВСЕ! См., например, http://closed.narod.ru/k/2/643.html.
Способ решения, предложенный в пособии по программированию на Паскале показался мне сложным

Паскаль при вычислении адреса элемента массива в оперативной памяти использует информацию об объявлении массива. По этой причине, когда мы выходим за пределы массива - этот адрес оказывается неправильным. Например, если использовать определение ar = array[1..1, 1..1], а работать с массивом, как будто он имеет размерность 6х6, то адрес элемента с индексами [1, 2] совпадает с адресом элемента [2, 1]. Одинаковые адреса в памяти имеют элементы [1, 4], [2, 3], [3, 1] и [4, 1].

Проверить это сможете, если наберете код такой программы:

type
   PMatrix = ^TMatrix;
   TMatrix = array[1..1, 1..1] of real;
var
  p: PMatrix;
  i, j: Integer;
  size: Integer;
const
  range: Integer = 6;

BEGIN
  size:= range*2*SizeOf(Real);
  GetMem(p, size);
  Writeln;
 {$R-}
  for i:=1 to range do begin
    for j:=1 to range do
      Write('[',i,',',j,']:', ofs(p^[i,j]):3,'   ') ;
    WriteLn
  end;
  FreeMem(p, size);
END.
Вы получите такое:
  [1,1]:   0   [1,2]:   6   [1,3]: 12   [1,4]: 18   [1,5]: 24   [1,6]: 30   
  [2,1]:   6   [2,2]: 12   [2,3]: 18   [2,4]: 24   [2,5]: 30   [2,6]: 36   
  [3,1]: 12   [3,2]: 18   [3,3]: 24   [3,4]: 30   [3,5]: 36   [3,6]: 42   
  [4,1]: 18   [4,2]: 24   [4,3]: 30   [4,4]: 36   [4,5]: 42   [4,6]: 48   
  [5,1]: 24   [5,2]: 30   [5,3]: 36   [5,4]: 42   [5,5]: 48   [5,6]: 54   
  [6,1]: 30   [6,2]: 36   [6,3]: 42   [6,4]: 48   [6,5]: 54   [6,6]: 60
Тут показаны адреса элементов в памяти. Точнее, показаны смещения. Сегмент у них один.

Выпуск что-то получается длинным ... .На этом пока прервусь. Способ решения дам позднее

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

Обращу Ваше внимание на то, что на нашем форуме www.yourpascal.com появилась возможность размещать свои файлы. При создании нового раздела это просто и естественно, а при ответе - нужны выбирать "ответ", а не "быстрый ответ"

Мы приглашаем Вас и Ваших друзей к сотрудничеству. Напишите, какая проблема Вас лично интересует - и мы постараемся помочь Вам. Поделитесь со всеми, если Вам удастся найти красивое решение. Присылайте свои программы, и если они хороши, то опубликуем их с обязательным указанием Вашего авторства.

По всем вопросам можно писать либо в Гостевую книгу нашего сайта на www.turbopascal.tk, либо

мне, автору этого выпуска, © Сурину Борису: surin_bp@mail.ru.

Постараюсь ответить на все вопросы и учесть все разумные предложения

Рассылка поддерживается сайтом www.turbopascal.tk. При перепечатке ссылка на сайт обязательна

Внимание: сессия и экзамены еще не начались - самое время подписаться на нашу рассылку:
Рассылки@Mail.ru
Шпаргалки

Hosted by uCoz