Загвоздка в динамических массивах

Модераторы: Duncon, Naeel Maqsudov, Игорь Акопян, Хыиуду

Ответить
warobushek
Сообщения: 7
Зарегистрирован: 14 апр 2008, 05:52

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

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

Вот это бы мне действительно помогло.
Нужно, чтобы обязательно в TURBO PASCAL 7.0 работало
BBB
Сообщения: 1298
Зарегистрирован: 27 дек 2005, 13:37

Если на паскале, то мы делали так. (Рассм. на примере массива, размер элемента кортороко больше 1 байта - так заметнее некоторые нюансы)

Объявляем тип данных - массив от 1 (вариант - от 0) до "много". Учитываем, что в Паскале размер не может превосходить $FFF0:

Код: Выделить всё

type
   {- Массивы и указатели на них -}
      tIntArr0 = array [0..$FFF0 div SizeOf(integer)-1] of integer;
      tIntArr1 = array [1..$FFF0 div SizeOf(integer)] of integer;
      tPtrIntArr0 = ^tIntArr0;
      tPtrIntArr1 = ^tIntArr1;
Далее объявляешь пременную - указатель на "большой массив".
Затем динамически при помощи GetMem выделяешь память.
Когда массив больше не нужен, освобождаешь при помощи FreeMem.

Код: Выделить всё

var pDynArray : tIntArr1;
      nSize : integer;
begin
  write ('Vvedite size: ');
  readln (nSize);

 {Тут, конечно, по хородему, надо проверить, чтобы введенное nSize не было
  большим настольо, чтобы требуемый размер памяти не превосходил $FFFF}

  if (nSize > ($FFF0 div SizeOf(integer))) then begin
    writeln ('Slishkom bolsjoe chislo!');
    Halt;
  end;

  GetMem (pDynArray, nSize * SizeOf (pDynArray^[1]));
 { Тут опять, по-хорошему, проверить, что память выделилась }

 { Далее действия как с обычны массивом }
  pDynArray^ [1] := 10;
  pDynArray^ [10] := 20;
 ..........................................

 { осовобождение памяти }
  FreeMem (pDynArray, nSize * SizeOf (pDynArray^[1]));

end.
warobushek
Сообщения: 7
Зарегистрирован: 14 апр 2008, 05:52

Работает хорошо, спасибо
Ответьте, пожалуйста еще на один вопрос:
вот в этом участке кода

Код: Выделить всё

type
   {- Массивы и указатели на них -}
      tIntArr0 = array [0..$FFF0 div SizeOf(integer)-1] of integer;
      tIntArr1 = array [1..$FFF0 div SizeOf(integer)] of integer;
      tPtrIntArr0 = ^tIntArr0;
      tPtrIntArr1 = ^tIntArr1;
в чем различие между типами tIntArr0 и tIntArr1, между указателями tPtrIntArr0 и tPtrIntArr1.
Можно ведь убрать один из типов?

на основе Вашего кода я написал процедуру вывода динамического массива:

Код: Выделить всё

procedure outmas(ptr: tPtrIntArr0; size: integer);
var
   i: integer;
   flag: byte;
begin
   flag:=0;{признак того, что был встречен не нулевой элемент}
   for i:=1 to size do
                      begin
                         if(flag<>0) then
                            write(ptr^[i],' ')
                         else
                            if(ptr^[i]<>0) then
                               begin
                                  write(ptr^[i],' ');
                                  flag:=1;
                               end;
                      end;
end;{outmas}
Как мне сделать так, чтобы эта процедура работала и для обычного массива (или указателя на него) любого размера?
BBB
Сообщения: 1298
Зарегистрирован: 27 дек 2005, 13:37

warobushek писал(а):в чем различие между типами tIntArr0 и tIntArr1, между указателями tPtrIntArr0 и tPtrIntArr1.
Можно ведь убрать один из типов?
Убрать - можно.
Различие - смотрите внимательнее на описанте типов. В первом случае элементы массив считается от индекса 1, во втором - от индекса 0. Кому-то (или в каких-то случаях) удобнее использовать один вариант, кому-то (или в каких-то случаях) - другой.

Кстати, в связи с этим в Вашем коде ошибка:
warobushek писал(а):

Код: Выделить всё

procedure outmas(ptr: tPtrIntArr0; size: integer);
......................................
begin
   for i:=1 to size do
      begin
         ..............
          write(ptr^[i],' ')
         ..............

      end;
end;{outmas}
Вы передаете в процедцру массив (т.е., конечно, указатель на него) типа tPtrIntArr0, т.е. считающийся от индекса 0, а цикл крутите от 1 до size.

Либо нужно использовать tPtrIntArr1 (и крутить цикл, как сейчас и написано, от 1 до size), либо, если использовать (как сейчас) tPtrIntArr0, то цикл крутить от 0 до (size - 1)

Кстати, читабельнее будет в качестве флага flag использовать переменную не типа byte, а типа boolean. Переменные которго принимают значения TRUE и FALSE. Хотя формально здесь ошибки нет.

Код: Выделить всё

var
   i: integer;
   flag: boolean;
begin
   flag:=FALSE;{признак того, что был встречен не нулевой элемент}
   for i:=1 to size do
                      begin
                         if (flag) then
                            write(ptr^[i],' ')
                         else
                            if(ptr^[i]<>0) then
                               begin
                                  write(ptr^[i],' ');
                                  flag:=TRUE;
                               end;
                      end;
end;{outmas}
Как мне сделать так, чтобы эта процедура работала и для обычного массива (или указателя на него) любого размера?
Если я правильно понял вопрос, то, например, вот так:

Код: Выделить всё

procedure outmas(ptr: tPtrIntArr1; size: integer);
begin
  ...................................
end;{outmas}

var aiTest : array [1 .. 10] of integer;

begin
  ...................................
  outmas (@aiTest, SizeOf (aiTest) div SizeOf (aiTest [1]));

 { Ну, или, если предыдущая строка выглядит слишком загадочно, то: }
  outmas (@aiTest, 10);
end.
Если на @aiTest компилятор будет выдавать ошибку 'Type mismatch', то, либо поставьте директиву компилятора {$T-} ( Type-Checked Pointers Switch), либо сделайте приведение типа при передаче параметра в outmas :
outmas (tPtrIntArr1(@aiTest), 10);
warobushek
Сообщения: 7
Зарегистрирован: 14 апр 2008, 05:52

Все пошло, спасибо.
Только остался еще один вопросик по :

Код: Выделить всё

type
   {- Массивы и указатели на них -}
      tIntArr0 = array [0..$FFF0 div SizeOf(integer)-1] of integer;
      tIntArr1 = array [1..$FFF0 div SizeOf(integer)] of integer;
      tPtrIntArr0 = ^tIntArr0;
      tPtrIntArr1 = ^tIntArr1;
В каком объеме выделяется память под "динамический массив"?.
0..$FFF0 - это ведь почти сегмент. Будет ли под весь массив выделяться целый сегмент 64К? и => немного массивов можно будет таким образом определить?
Или, все-таки, внутри этого сегмента еще может быть определен другой массив?
BBB
Сообщения: 1298
Зарегистрирован: 27 дек 2005, 13:37

warobushek писал(а):В каком объеме выделяется память под "динамический массив"?. 0..$FFF0 - это ведь почти сегмент. Будет ли под весь массив выделяться целый сегмент 64К? и => немного массивов можно будет таким образом определить?
Или, все-таки, внутри этого сегмента еще может быть определен другой массив?

Вот это:

Код: Выделить всё

type
   {- Массивы и указатели на них -}
      tIntArr0 = array [0..$FFF0 div SizeOf(integer)-1] of integer;
      tIntArr1 = array [1..$FFF0 div SizeOf(integer)] of integer;
лишь ОПИСАНИЕ ТИПА. А не декларация переменной. Описание типа не выделяет память. Реальное выделение памяти происходит при вызове процедуры GetMem в размере, указываемом вторым параметром пр-ры.

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

Код: Выделить всё

type
      tDynIntArr1 = array [1..1] of integer;
Дальнейшие манипуляции абсолютно ничем не отличались. Единственное, надо отключить директиву компилятора " Range-Checking": {$R-}
Если этого не сделать, то обращение к любому элементу (уже динамически выделенного массива) вызовет ошибку времени выполнения. Так как, несмотря на то, что Вы выделили там достаточно (для ваших целей) места, формально (согласно описанию типа) этот массив содержит лишь один элемент. И run-time-проверка Range-Checking-га это заметит и вызовет аварийное завершение программы.

Поскольку динамическое выделение памяти происходит из "хипа", то, как мне представляется, можно навыделять столько массивов (если речь идет о Паскале, а не о Win32-приложениях, каждый размером, как справедливо замечено, не более 64 К), сколько в этом "хипе" хватит памяти.
Аватара пользователя
somewhere
Сообщения: 1858
Зарегистрирован: 31 авг 2006, 17:14
Откуда: 71 RUS
Контактная информация:

&quot писал(а): сколько в этом "хипе" хватит памяти.
Т.е. примерно около 500 кило.
It's a long way to the top if you wanna rock'n'roll
BBB
Сообщения: 1298
Зарегистрирован: 27 дек 2005, 13:37

somewhere писал(а):
сколько в этом "хипе" хватит памяти
Т.е. примерно около 500 кило.
Помню, под DOS-ом удавалось, позапихав все в верхнюю (или расширенную) память даже более 600 получать. А GUI Borland Pascal-я и сама туда грузится.

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

А уж если кмпилироваться под
Target platform: Protected mode Application,
то вообще ух какое раздолье наступает! :)

Тут, правда, другие подводные камни. Если используются всякие библиотеки типа Turbo/Object Professional, в которых немало asm-овских процедур, то компилироваться-то это все компилируется, но периодически падает по протекшену из-за ограничений защищенного режима.
warobushek
Сообщения: 7
Зарегистрирован: 14 апр 2008, 05:52

Спасибо БОЛЬШОЕ!!!
Теперь, вроде нет вопросов! ;)
sasha3050
Сообщения: 26
Зарегистрирован: 04 окт 2008, 23:13

Пожалуйста помогите очень нужно!!!!!!!!!Написать программу на Paskal, которая вычисляет среднюю длину слов в файле, который содержит текст.В данном случае будет считатьтся любая строка символов, которая начинается и заканчивается одним с следующих елеметов: пробел, запятая, точка, начало строки и конец строки.В программе должна быть обозначена функция, которая получает в качестве аргумента поток ввода с файла.
Ответить