Учебник по программированию.

Первые шаги. Язык программирования PascalABC.

Предыдущий параграф Назад в содержание Следующий параграф


Глава VII. Заключительная часть.


Если вы дочитали книгу, до данного параграфа, изучили все приведённые примеры, выполнили все предложенные задания, то к настоящему времени вы должны были узнать практически все возможности языка PascalABC.NET и получить хороший опыт программирования. Поэтому дальнейшее ваше развитие по большей части будет заключаться в изучении уже готовых классов, которые предоставляют разработчики программных продуктов для программирования.

В PascalABC.NET существует ряд классов, которыми вы можете пользоваться. Если подробно расписывать, как с ними работать, то может получиться ещё одна книга. Поэтому в рамках данного учебника будет проведён просто их обзор с небольшими примерами. Их изучение вы должны провести самостоятельно, т.к. в справочной системе они достаточно хорошо и понятно описаны. Думаю, что если вы не просто прочитали учебник, а выполнили самостоятельно все предложенные задания, то изучение этих классов не составит вам труда.

Данная глава является заключительной. Поэтому прежде чем сделать обзор нам необходимо изучить ещё две оставшиеся темы это «Динамическая память» и «Исключения». Возможно, на первых порах вы не будете пользоваться данными темами, однако если вы станете настоящими программистоми, то будете применять их постоянно. В любом случае, вы должны иметь представление об этих темах и уметь применять их на практике.



§30. Динамическая память.

Динамическая память, указатели.

Все переменные, которые мы до сих пор создавали, хранятся в том же участке памяти, который отводится под память программы. Если нам понадобиться переменная, требующая большой объём памяти, то её необходимо хранить уже отдельно от программы, т.к. под программу, как правило, отводится не так много места.

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

Память, не занятая программой и в которой можно хранить переменные, называется динамической. Для того, что бы создать переменную, хранящуюся в динамической памяти отдельно от самой программы, используется указатель. Указатель это переменная, хранящая в себе адрес кусочка оперативной памяти и его размер. Размер кусочка этой памяти соответствует типу переменной хранящейся в данном кусочке.  Другими словами указатель указывает на какую-либо переменную, хранящуюся в оперативной памяти. Отсюда и название «указатель».

Объявляется указатель так же как и обычная переменная в разделе объявления переменных или в теле программы следующим образом:


var pi: ^integer;


Как видно из кода отличие составляет только символ «^» перед типом. Так же стоит сказать, что перед именем указателя, как правило, но не обязательно, ставится префикс p (от слова pointer-указатель).

Так как указатель переменная, хранящая в себе адрес кусочка памяти, то при обращении к ней мы получим как раз этот адрес. Однако просто после объявления указатель ещё не на что не указывает, и вместо адреса мы получим константу nil:


begin

  writeln(pi);

end.

_____________________

nil


Константа nil означает, что указатель на данный момент пуст и ни на что не указывает. Так же если мы присвоим указателю эту константу, то мы его освободим, и он перестанет на что-либо указывать. Однако из-за ряда причин таким способом пользоваться не рекомендуется. Для освобождения указателя существует специальная процедура, о которой будет сказано позже.

Для того, что бы заполнить указатель, и он начал указывать на определённый кусок оперативной памяти, эту память нужно выделить специальной процедурой new. В качестве параметра ей нужно передать как раз нужный нам указатель. Т.е. данная процедура выделяет кусочек памяти и присваивает адрес этого кусочка указателю, который передан в качестве параметра:

begin

  new(pi);

  writeln(pi);

end.

_________________

$181FF8


В последней строчке примера находится не понятная строка. Здесь  стоит сделать небольшое отступление. Каждая ячейка любой компьютерной памяти имеет свой порядковый номер. Именно этот номер и является её адресом. Если в компьютере 4 Гб оперативной памяти, то соответственно  последняя ячейка будет иметь адрес, который можно обозначить десятизначным десятеричным числом. Т.е. если выражать адреса десятеричными числами, то придётся оперировать со слишком большим количеством цифр. Понятно, что это неудобно. Поэтому, как правило, все адреса выражаются шестнадцатеричными числами. Они несколько короче десятеричных. О том, что такое десятеричное или шестнадцатеричное число, надеюсь, вы знаете. Так вот в последней строке примера как раз находится шестнадцатеричное число, которое означает адрес выделенного кусочка памяти. О том, что это именно шестнадцатеричное число, а не какое-либо другое, говорит символ «$», стоящий перед числом. На том, что обозначают буквы в таких числах, здесь останавливаться не будем.

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


begin

  new(pi);

  writeln(pi);

  dispose(pi);

  writeln(pi);

end.

_________________

$17A110

nil


Мы научились выделять и освобождать кусочки оперативной памяти, теперь осталось научиться записывать туда данные и считывать их оттуда. Для этого используется так называемая операция разыменования. В коде программы для разыменования указателя необходимо после его имени поставить символ «^»:


begin

  new(pi);

  pi^:=30;

  writeln(pi^);

end.

________________

30


Как видно работа с разыменованным указателем аналогична работе с обычной переменной.

Указатели в PascalABC.NET делятся на типизированные и бестиповые. До сих пор мы изучали и работали с типизированными указателями, т.е. с ними вы уже знакомы. В отличии от них бестиповые указатели содержат в себе только адрес. Для их объявления используется слово pointer:


var p:pointer;


Операция разименования к такому указателю не применима.

Значение одного указателя можно присвоить другому. Так же указатели можно сравнивать друг с другом на равенство (=) или (<>), для того, что бы определить указывают ли они на один объект. Далее пример:


var i:integer;

    p1,p2: ^integer;

begin

  i:=3;

  p1:=@i;

  p2:=p1;

  if p1=p2 then writeln('Указатели указывают на одну и ту же переменную');

end.

_________________________________________________________________________

Указатели указывают на одну и ту же переменную


Теперь стоит сказать про операцию @, которая возвращает адрес объекта. Если поставить символ «@» пред именем переменной, то мы получим не значение этой переменной, а адрес кусочка памяти, в котором находится эта переменная. Этот адрес можно присвоить указателю и работать с переменной через указатель. Делается это следующим образом:


var i:integer;

    p: ^integer;

begin

  i:=3;

  p:=@i;

  writeln(p^);

end.

________________

3

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


procedure P(p1,p2:^integer);

begin

  p1^:=Random(1,100);

  p2^:=Random(1,100);

end;


begin

  var i1,i2:integer;

  P(@i1,@i2);

  writeln('i1=',i1,' i2=',i2);

end.


Такой подход использовался раньше в старых версиях языка Pascal. Теперь в PascalABC.NET он не актуален, т.к. вместо указателей можно использовать слово var при объявлении подпрограммы:


procedure P(var ii1,ii2:integer);

begin

  ii1:=Random(1,100);

  ii2:=Random(1,100);

end;


begin

  var i1,i2:integer;

  P(i1,i2);

  writeln('i1=',i1,' i2=',i2);

end.


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


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

Теперь, когда мы рассмотрели данную тему, хочется поговорить об объектах. Для создания объекта необходимо вначале объявить переменную определённого класса, а затем вызвать конструктор этого класса. Так обстоит дело потому, что объекты хранятся не в памяти программы, а в динамической памяти. В памяти программы хранится переменная типа данного класса, а сам объект созданный конструктором, хранится именно в динамической памяти. По своей сути переменная типа класса является указателем на объект. Думаю, после этих слов вам стало ясно, почему при создании объекта необходимо помимо объявления переменной ещё создать объект с помощью конструктора.

У вас может возникнуть уместный вопрос, если объект можно создать в теле программы, то возможно ли его уничтожить так же в теле программы? На самом деле в любом классе есть метод, который называется деструктором. Он как раз и должен заниматься уничтожением объекта. Однако в PascalABC.NET из-за некоторых особенностей языка этот метод не работает. Поэтому даже если вы опишите его в классе, то он будет работать как обычный метод и объект уничтожаться не будет.

Однако, если вам будет очень необходимо освободить память, то можете присвоить переменной указателю на объект константу nil. В таком случае память, занятая под объект освободиться, но объект при этом уничтожен не будет. Указатель, в свою очередь, не перестанет указывать на эту память, соответственно и на объект. И пока на место этой памяти не запишется что-либо другое, можно будет пользоваться объектом. Поэтому может создаться иллюзия, что после присвоения указателю на объект константы nil, ничего не произошло. Однако если вы будете продолжать пользоваться объектом через этот же указатель, и если на его место будет записано, что-либо новое, то возникнет ошибка.


Динамический список.

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

Предлагаю, прежде чем читать дальше, подумать самостоятельно над вопросом: как можно организовать динамический список целых чисел? Сделаю подсказку. Динамический список можно организовать с помощью использования указателей. Если у вас появятся какие-либо идеи, то попробуйте их реализовать, прежде чем читать дальше. Думаю, в таком случае материал уложится лучше, и вам будет интереснее.

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



Теперь осталось только реализовать этот принцип в коде программы. Так как всё современное программирование не безосновательно построено на принципах ООП, то и список предлагаю оформить в виде класса TSpisok. Это даст возможность без особого труда создавать сколь угодно много объектов-списков, а так же можно будет использовать методы этого класса для работы с ними.

Далее представлено описание получившегося класса TSpisok и код программы, проверяющей работу этого класса:


program Spisok;

type TElement = record

       Data:integer;

       p:pointer;

     end;


type TSpisok = class

      public

       constructor ;

         begin

           new(pEl);

           P1:=pEl;

         end;

       procedure write(i:integer);

         begin

           pEl^.Data:=i;

           new(pSled);

           pEl^.p:=pSled;

           pSled^.p:=nil;

           pEl:=pSled;

         end;

       procedure Nachalo;

         begin

           pEl:=P1;

         end;

       function EOS:boolean;//Конец списка

          begin

            if pEl^.p=nil then EOS:=true

              else EOS:=false;

          end;

       function read:integer;

         begin

           read:=pEl^.Data;

           pEl:=pEl^.p;

         end;

      protected

       pEl,pSled:^TElement;

       P1:pointer;

     end;


var Sp:TSpisok:=new TSpisok;


begin

  for var i:=1 to random(1,10) do

    begin

      var temp:=random(1000);

      Sp.write(temp);

      writeln(i,'   temp=',temp);

    end

  Sp.Nachalo;

  while not Sp.EOS do

    writeln(Sp.read);

end.

___________________________________________

1   temp=944

2   temp=78

3   temp=824

4   temp=122

5   temp=692

6   temp=591

7   temp=342

944

78

824

122

692

591

342


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


Динамические массивы.

Те массивы, которыми мы пользовались, называются статическими, т.к. заранее известно количество элементов массивов и память под них выделяется во время загрузки программы и её объём не меняется в течение работы всей программы.

Есть возможность создавать и определять количество элементов массива во время работы программы. Такие массивы называются динамическими.

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


var dm_1:array of integer;//одномерный массив

    dm_2:array [,] of integer;//двухмерный массив


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


  dm_1:=new integer[5];

  dm_2:=new integer[3,4];


После операции new ставиться тип элементов и в квадратных скобках их количество. После создания динамического массива им можно пользоваться как обычным (статическим). Здесь будьте внимательны, нумерация в динамических массивах начинается с нуля, поэтому если вы указали количество элементов 5, то индекс последнего элемента будет 4, а индекс первого элемента 0. Далее пример:


var dm_1:array of integer;//одномерный массив

    dm_2:array [,] of integer;//двухмерный массив


begin

  var raz_1:=random(2,4);

  var raz_2:=random(2,4);

  var raz_3:=random(2,4);

  dm_1:=new integer[raz_1];

  dm_2:=new integer[raz_2,raz_3];

//Заполняем массивы

  for var i:=0 to raz_1-1 do

    dm_1[i]:=random(1000);

  for var i:=0 to raz_2-1 do

    for var j:=0 to raz_3-1 do

      dm_2[i,j]:=random(1000);

//Выводим массивы на экран

  for var i:=0 to raz_1-1 do

    write(dm_1[i]:4,'  ');

  writeln;

  writeln;

  for var i:=1 to raz_2-1 do

    begin

      for var j:=1 to raz_3-1 do write(dm_2[i,j]:4,'  ');

      writeln;

    end;

end.

___________________________________________________________

389   449   168   444 


325   298 



Существует ещё второй способ создания динамических массивов с помощью процедуры SetLength. Вместо следующих строчек предыдущего примера:


  dm_1:=new integer[raz_1];

  dm_2:=new integer[raz_2,raz_3];


мы можем написать:

 

  SetLength(dm_1,raz_1);

  SetLength(dm_2,raz_2,raz_3);


результат работы программы не измениться.

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


  SetLength(dm_1,raz_1+2);

  SetLength(dm_2,raz_2+2,raz_3+2);

//Выводим массивы на экран

  for var i:=0 to raz_1+1 do

    write(dm_1[i]:4,'  ');

  writeln;

  writeln;

  for var i:=1 to raz_2+1 do

    begin

      for var j:=1 to raz_3-1 do write(dm_2[i,j]:4,'  ');

      writeln;

    end;

_________________________________________________________

389   449   168   444     0     0 


325   298 

   0     0 

   0     0 


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

Ещё есть возможность создавать массив массивов. Т.е. массив, элементами которого являются массивы. О том, как работать с динамическим массивом массивов при необходимости можете прочитать в разделе справки: «Справочник по языку -> Типы данных -> Динамические массивы».

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


В данном параграфе мы познакомились с принципами работы с динамической памятью. Соответственно, узнали, что такое указатели и динамические массивы. Плюсом ко всему написали класс TSpisok, который реализует динамический список.

Задачи.

1. Усовершенствовать класс TSpisok, добавив свойство, содержащее количество элементов, свойство содержащее значение текущего элемента списка и следующие методы:

  • метод, позволяющий считать элемент по его порядковому номеру;
  • метод, меняющий содержание элемента по номеру;
  • метод, добавляющий элемент в список в определённое место;
  • метод, удаляющий элемент с определённым номером из списка;
  • метод, очищающий весь список.

Для усовершенствования класса создать класс потомок от класса TSpisok.

Вставить комментарии описывающие работу, написанных вами свойств и методов.

Написать программу, демонстрирующую работу данного класса.

2. Решить следующую задачу: уравнение работы электродвигателя имеет следующий вид:

,

где момент, развиваемый двигателем, частота вращения вала двигателя.

Изменение частоты вращения находится из выражения:

,

где момент сопротивления, создаваемы нагрузкой, время за которое произошло изменение частоты. Для расчётов  принять равным 0.0001. Момент сопротивления принять от 100 до 1000 по желанию.

Рассчитать запуск двигателя с нулевой до максимальной скорости. Сохранить результаты промежуточных вычислений: время , момент двигателя  , частота вращения , массиве.

Пояснение: изначально система находится в покое, соответственно .

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

.

Дальше делаем второй шаг: ; ;

.

Так продолжаем до тех пор, пока система не уравновесится. Критерием уравновешенности является равенство и .

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


Решение.

1.

program ModernSpisok;

type TElement = record

       Data:integer;

       p:pointer;

     end;


type TSpisok = class

       ...............

     end;


type TModernSpisok = class (TSpisok)

      public

       //Содержит текущий элемент

       property TekElem:integer write write read read;

       //Возвращает количество элементов в списке

       function KolElem:integer;

         begin

           var i:=0;

           Nachalo;

           while not EOS do

             begin

               pEl:=pEl^.p;

               Inc(i);

             end;

           KolElem:=i;

         end;

       //Возвращает значение элемента по номеру 

       function ElemPoNomeru(n:integer):integer;

         begin

           Poziciya(n);

           ElemPoNomeru:=pEl^.Data;

         end;

       //Меняет значение элемента с номером n 

       procedure PerepisElPoNomeru(n,dat:integer);

         begin

           Poziciya(n);

           pEl^.Data:=Dat;

         end;

       //Вставляет новый элемент на место старого с номером n

       //при этом весь список сдвигается

       procedure Vstavit(n,dat:integer);

         begin

           Poziciya(n);

           new(PSled);

           pSled^.p:=pEl^.p;

           pSled^.Data:=pEl^.Data;

           pEl^.Data:=dat;

           pEl^.p:=pSled;

         end;

       //Удаляет из списка элемент с номером n 

       procedure Udalit(n:integer); 

         begin

           if (n>1) and (n<>KolElem) then

             begin//Если элемент где-то в середение

               Poziciya(n);

               pSled:=pEl^.p;

               dispose(pEl);

               Poziciya(n-1);

               pEl^.p:=pSled;

             end

           else

             if n=1 then

               begin//если удаляем первый элемент

                 Poziciya(1);

                 P1:=pEl^.p;

                 dispose(pEl);

               end

              else

               begin//если удаляем последний элемент

                 Poziciya(n);

                 pEl^.p:=nil;

                 Poziciya(n+1);

                 Dispose(pEl)

               end

         end;

       //Очищает весь список 

       procedure Ochistit;

         begin

           Nachalo;

           var pTemp:pointer;

           pTemp:=pEl^.p;

           pEl^.p:=nil;

           pEl:=pTemp;

           while pEl^.p<>nil do

             begin

               pTemp:=pEl^.p;

               dispose(pEl);

               pEl:=pTemp;

             end;

         end;

      protected

        procedure Poziciya(n:integer);//Переход на позицию n

          begin

            Nachalo;

            var i:integer:=1;

            for i:=1 to n-1 do pEl:=pEl^.p;

          end;

     end;


var Sp:TModernSpisok:=new TModernSpisok;


begin

  writeln('В списке ',Sp.KolElem,' элементов.');

  writeln('Создаём случайный список:');

  var max:=random(1,10);//Количество элементов списка

//Заполняем список

  for var i:=1 to max do

    begin

      var temp:=random(1000);

      Sp.TekElem:=temp;

      writeln(i:2,'   temp = ',temp);

    end

//Выводим список на экран

  writeln('Выводим список на экран:');

  Sp.Nachalo;

  while not Sp.EOS do write(Sp.TekElem,'  ');

  writeln;

  writeln('Теперь в списке ',Sp.KolElem,' элементов.');

//Выводим элемент с номером temp

  var temp:=Random(1,max);

    Writeln('Элемент с номером ',temp,' равен ',Sp.ElemPoNomeru(temp));

//Меняем значение этого элемента

    Sp.PerepisElPoNomeru(temp,Random(1,1000));

//Снова выводим этот элемент

    Writeln('После изменения элемент с номером ',temp,' равен '

                                                 ,Sp.ElemPoNomeru(temp));

//Вставляем элемент

    Sp.Vstavit(temp,Random(1,1000));

//Выводим новый список 

  writeln('После вставки нового элемента на место ',temp,

                                 ' список выглядит следующим образом:');

  Sp.Nachalo;

  while not Sp.EOS do write(Sp.TekElem,'  ');

  writeln;

//Удаляем элемент под номером random(1,max) и выводим новый список

  temp:=Random(1,max+1);

    Sp.Udalit(temp); 

  writeln('После удаления элемента под номером ',temp,' список выглядит'+

                                                  ' следующим образом:');

//Выводим список

  Sp.Nachalo;

  while not Sp.EOS do write(Sp.TekElem,'  ');

  writeln;

//Очищаем список и для проверки вызываем метод Sp.KolElem

  Sp.Ochistit;

  writeln('После очистки списка колчичество элементов в нём равно - ',

                                                   IntToStr(Sp.KolElem));

end.

_________________________________________________________________________

В списке 0 элементов.

Создаём случайный список:

1   temp = 31

2   temp = 719

3   temp = 790

4   temp = 78

5   temp = 597

6   temp = 254

Выводим список на экран:

31  719  790  78  597  254 

Теперь в списке 6 элементов.

Элемент с номером 3 равен 790

После изменения элемент с номером 3 равен 412

После вставки нового элемента на место 3 список выглядит следующим образом:

31  719  684  412  78  597  254 

После удаления элемента под номером 2 список выглядит следующим образом:

31  684  412  78  597  254 

После очистки списка колчичество элементов в нём равно 0


2.

program ZapuskDvigatela;

const dt=0.0001;


type TData = record

       t,w,Md:real;

     end


var Mc:real; mData:array of TData;


begin

  Mc:=Random(100,1000);

  writeln('Mc=',Mc);

  SetLength(mData,1);

  var i:integer:=0;

  mData[i].t:=0;

  mData[i].w:=0;

  mData[i].Md:=15372/

                 ((1-mData[i].w/314)/0.07+0.07/(1-mData[i].w/314)+0.196);

  while (mData[i].Md-Mc)>0.001 do

    begin

      Inc(i);

      SetLength(mData,i+1);

      mData[i].t:=mData[i-1].t+dt;

      mData[i].w:=mData[i-1].w+(1/0.6)*(mData[i-1].Md-Mc)*dt;

      mData[i].Md:=15372/

                 ((1-mData[i].w/314)/0.07+0.07/(1-mData[i].w/314)+0.196);

    end;

  writeln('t':9,'w':9,'Md':9);

for i:=0 to length(mData)-1 do 

      writeln(mData[i].t:9:3,mData[i].w:9:3,mData[i].Md:9:3);

end.

_________________________________________________________________________

Mc=808

        t        w       Md

    0.000    0.000 1056.370

    0.000    0.041 1056.506

    0.000    0.083 1056.643

    0.000    0.124 1056.779

    0.000    0.166 1056.915

    .......................

    0.017    7.469 1081.500

    .......................

    0.023   10.299 1091.332

    .......................

    0.033   14.912 1107.749

    .......................

    0.054   26.394 1150.829

    .......................

    0.095   53.076 1265.057

    .......................

    0.125   79.528 1402.908

    .......................

    0.156  115.072 1642.853

    .......................

    0.211  263.519 5249.811

    .......................

    0.222  312.827  809.629

    .......................

    0.229  312.829  808.001


Примечание: на практике с помощью подобной программы: во-первых можно узнать время запуска двигателя при определённой нагрузке; во-вторых, используя промежуточные данные, можно проследить изменение момента на валу двигателя и построить график. Используя заполненный динамический массив, можно записать данные в файл для сохранения.




Предыдущий параграф Назад в содержание Следующий параграф