Как сделать из динамического массива статический

Обновлено: 07.07.2024

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

Тип динамического массива конструируется следующим образом:

array of тип элементов (одномерный массив)
array [,] of тип элементов (двумерный массив)
и т.д.

Переменная типа динамический массив представляет собой ссылку. Поэтому динамический массив нуждается в инициализации (выделении памяти под элементы).

Выделение памяти под динамический массив

Для выделения памяти под динамический массив используется два способа. Первый способ использует операцию new в стиле вызова конструктора класса:

var
a: array of integer;
b: array [,] of real;
begin
a := new integer[5];
b := new real[4,3];
end.

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

var
a: array of integer := new integer[5];
b: array [,] of real := new real[4,3];

Описание типа можно при этом опускать - тип автовыводится:

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

Элементы массива при этом заполняются значениями по умолчанию.

Процедура SetLength обладает тем преимуществом, что при ее повторном вызове старое содержимое массива сохраняется.

Инициализация динамического массива

Можно инициализировать динамический массив при выделении под него память операцией new:

Инициализацию динамического массива в момент описания можно проводить в сокращенной форме:

var
a: array of integer := (1,2,3);
b: array [,] of real := ((1,2,3),(4,5,6),(7,8,9),(0,1,2));
c: array of array of integer := ((1,2,3),(4,5),(6,7,8));

При этом происходит выделение памяти под указанное справа количество элементов.

Инициализация одномерного массива проще всего осуществляется стандартными функциями Seq. которые выделяют память нужного размера и заполняют массив указанными значениями:

var a := Arr(1,3,5,7,8); // array of integer
var s := Arr('Иванов','Петров','Сидоров'); // array of string
var b := ArrFill(777,5); // b = [777,777,777,777,777]
var r := ArrRandom(10); // заполнение 10 случайными целыми в диапазоне от 0 до 99

В таком же стиле можно инициализировать массивы массивов:

var a := Arr(Arr(1,3,5),Arr(7,8),Arr(5,6)); // array of array of integer

Длина динамического массива

Динамический массив помнит свою длину (n-мерный динамический массив помнит длину по каждой размерности). Длина массива (количество элементов в нем) возвращается стандартной функцией Length или свойством Length :

Для многомерных массивов длина по каждой размерности возвращается стандартной функцией Length с двумя параметрами или методом GetLength(i) :

Ввод динамического массива

После выделения памяти ввод динамического массива можно осуществлять традиционно в цикле:

Ввод динамического массива можно осуществлять с помощью стандартной функции ReadSeqInteger:

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

Вывод динамического массива

Процедура write выводит динамический массив, заключая элементы в квадратные скобки и разделяя их запятыми:

n-мерный динамический массив выводится так, что каждая размерность заключается в квадратные скобки:.

Динамический массив можно выводить также методом расширения Print или Println:

При этом элементы по умолчанию разделяются пробелами, но можно это изменить, задав параметр Print, являющийся разделителем элементов. Например:

выводит каждый элемент на отдельной строке.

Массивы массивов

Если объявлен массив массивов

var с: array of array of integer;

то его инициализацию можно провести только с помощью SetLength :

SetLength(с,5);
for i := 0 to 4 do
SetLength(c[i],3);

Для инициализации такого массива с помощью new следует ввести имя типа для array of integer :

type IntArray = array of integer;
var с: array of IntArray;
.
c := new IntArray[5];
for i := 0 to 4 do
c[i] := new integer[3];

Инициализацию массива массивов можно также проводить в сокращенной форме:

var
c: array of array of integer := ((1,2,3),(4,5),(6,7,8));

Присваивание динамических массивов

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

var a1: array of integer;
var a2: array of integer;
a1 := a2;

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

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

Передача динамического массива в подпрограмму

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

procedure Squares(a: array of integer);
begin
for var i:=0 to a.Length-1 do
a[i] := Sqr(a[i]);
end;

begin
var a := Arr(1,3,5,7,9);
Squares(a);
end.

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

procedure Add(var a: array of integer; x: integer);
begin
SetLength(a,a.Length+1);
a[a.Length-1] := x;
end;

begin
var a := Arr(1,3,5,7,9);
Add(a,666);
writeln(a);
end.

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

Скажем, я хочу присвоить значения этому массиву:

Что именно происходит, чтобы вызвать эту ошибку?

Решение

Это не работает, потому что вы назначаете статический массив указателю.

Это два разных типа данных для компилятора.

Когда ты сказал:

Другие решения

Вы должны использовать std::copy скопировать статический массив в динамический массив, как в примере ниже:

В качестве альтернативы, если вам нужно удобство присваиваний вместо поэлементного копирования и при условии, что вы знаете размер массивов во время компиляции и ваш компилятор поддерживает функции C ++ 11, используйте std::array как в примере ниже:

Тем не менее, рекомендуется отдать предпочтение std::vector над использованием необработанных динамических массивов, как в примере ниже:

Вы можете использовать std::vector у которого есть конструктор итератора, это решит проблему для вас.

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

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

Цель лекции: изучить объявления, выделения и освобождения памяти для одномерных динамических массивов, обращения к элементам, научиться решать задачи с использованием одномерных динамических массивов в языке C++.

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

В соответствии со стандартом языка массив представляет собой совокупность элементов, каждый из которых имеет одни и те же атрибуты. Все эти элементы размещаются в смежных участках памяти подряд, начиная с адреса, соответствующего началу массива. То есть общее количество элементов массива и размер памяти, выделяемой для него, получаются полностью и однозначно заданными определением массива. Но это не всегда удобно. Иногда требуется, чтобы выделяемая память для массива имела размеры для решения конкретной задачи, причем ее объем заранее не известен и не может быть фиксирован. Формирование массивов с переменными размерами (динамических массивов) можно организовать с помощью указателей и средств динамического распределения памяти .

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

Объявление одномерных динамических массивов

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

ИмяМассива – идентификатор массива, то есть имя указателя для выделяемого блока памяти .

Тип – тип элементов объявляемого динамического массива . Элементами динамического массива не могут быть функции и элементы типа void .

В данных примерах a и d являются указателями на начало выделяемого участка памяти. Указатели принимают значение адреса выделяемой области памяти для значений типа int и типа double соответственно.

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

Выделение памяти под одномерный динамический массив

Для того чтобы выделить память под одномерный динамический массив в языке С++ существует 2 способа.

1) при помощи операции new , которая выделяет для размещения массива участок динамической памяти соответствующего размера и не позволяет инициализировать элементы массива.

ИмяМассива – идентификатор массива, то есть имя указателя для выделяемого блока памяти .

Тип – тип указателя на массив .

ВыражениеТипаКонстанты – задает количество элементов ( размерность) массива . Выражение константного типа вычисляется на этапе компиляции.

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

2) при помощи библиотечной функции malloc (calloc) , которая служит для выделения динамической памяти.

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

Они занимают область в памяти. Компилятор резервирует соответствующий объем памяти для каждого типа и количества элементов каждого массива.

Чтобы зарезервировать память при помощи компилятора для статического массива, необходимо указать ему сколько в нем будет элементов целых чисел. Для этого используется объявление int a [] ;

Осторожно! Если преподаватель обнаружит плагиат в работе, не избежать крупных проблем (вплоть до отчисления). Если нет возможности написать самому, закажите тут.

Пример объявления для 12 элементов:

Для динамического массива количество элементов не указывают.

На этапе написания программы размер динамических массивов неизвестен. Ввод количества элементов осуществляется во время работы программы.

Размером при объявлении динамического массива является числовая переменная, а не константа.

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

В динамических массивах также не указывают длину строк и их количество. Границы массивов полностью контролируются разработчиком.

Указатели для динамических массивов

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

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

Действия с указателями

Указатели:

Чтобы объявить указатель, объявляют тип переменной и ставят перед именем символ *. Указанная переменная должна быть одного типа с указателем, который на ее указывает.

Указатели:

Адрес присваивается указателю с помощью оператора &. Знак &, стоящий перед переменной, будет указывать на ее адрес.

  1. Получение значения по этому адресу.

Указатели:

Для переменной определенного типа, на которую определен указатель, например, pF, указывается число соответствующего типа. Символ * перед pF означает информацию ячейки, указанной pF.

Указатели:

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

Указатели:

Указатель указывает на недействительный адрес, который равен нулю, соответственно, указатель тоже недействительный. Запись по нему не может быть осуществлена.

Указатели:

  • int i, *pI;
  • printf("Адр.i =%p", pI);
  • pI = &i;

Для вывода указателей на экран применяют формат %p.

Ошибки, возникающие при неверном использовании динамических массивов

  1. Запись в чужую область памяти.

Причина: Выделение памяти произошло неудачно, при этом массив используется.

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

Причина: Массив уже удален и теперь удаляется снова.

Вывод: если массив удален из памяти, необходимо обнулить показатель, так можно быстрее выявить ошибку.

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

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

Виды динамических массивов

Классификация динамических массивов:

Одномерный динамический массив представляет собой множество элементов одного типа данных.

Двухмерный массив имеет более сложную структуру и содержит в качестве элементов массива другие массивы. Каждому хранящемуся в массиве А указателю присвоено не одно значение, а одномерный динамический массив таких значений.

Переменная А сама является указателем и объявляется как указатель на указатель на нужный тип. Данный указатель нужно инициализировать, то есть положить ему адрес массива адресов. В памяти программы должен существовать массив адресов указателей на int и сам массив указателей на int. В этом массиве указателей должны быть разложены адреса, возможно лежащие отдельно друг от друга в памяти.

Пример двумерного массива:

Выделение памяти под двумерный массив

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

Например, есть указатель А: int ** A ;

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

A = (int **) malloc (N * sizeof(int *)). Здесь важно указать тип указателя, а не int. Потому что в этом массиве, на который указывает A, будут храниться указатели, а не int.

Далее инициализируют уже сами элементы этого массива указателей путем запуска цикла:

for (int i = 0 ; i Пример

При этом не указывают размерность массива. Операция delete освобождает область памяти, выделенную при помощи оператора new. Таким образом, она возвращает системе эту область памяти для дальнейшего распределения. Чтобы освободить память, которая была динамически выделена предыдущим оператором, применяют оператор delete newPtr;

Иногда выделение памяти под динамические массивы осуществляют с помощью двух языков Си и Си++ одновременно: операции new и функции malloc.

int *a = new int[n];

double *b = (double *)malloc(n * sizeof (double));

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

В третьей строке применяется функция malloc, которая в данном случае выделяет память под n элементов типа double. Для того, чтобы освободить память, выделенную с помощью функции malloc, применяют функцию free. Память не обнуляется при ее выделении. Динамический массив нельзя инициировать.

Перераспределение памяти

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

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

Функция realloc осуществляет перераспределение памяти. Она меняет размер динамически выделяемой области памяти, адресуемой указателем ptr, на новый размер size.

Realloc может возвращать адрес новой области памяти:

realloc(void *ptr, size_t size);

Если ptr равен NULL, то realloc ведет себя подобно функции malloc.

Поведение функции realloc будет неопределенным, если:

  • значение ptr не было возвращено функциями calloc, malloc или realloc;
  • пространство памяти было освобождено функцией free, и ptr указывают на него.

Size указывают в абсолютном значении.

Важные моменты в перераспределении памяти с помощью функции realloc:

  1. Если существующее пространство меньше по размеру, чем size, то в конце будет выделяться новое неинициализированное непрерывное пространство, при этом предыдущие данные пространства будут сохранены.
  2. Значение, равное NULL, будет возвращено, если realloc не может выделить запрашиваемое пространство, а в указанном ptr пространстве содержимое остается нетронутым.
  3. Realloc будет действовать, как функция free, если size=0, а ptr не равен NULL.
  4. Если первым параметром функции realloc использовать NULL, то можно получить эффект, который достигается с помощью функции malloc с тем же значением size.

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

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

Пример использования функции realloc:

p2 = realloc(p1, new_size);

if (p2 1= NULL) p1 = p2;

Необходимо учитывать, что realloc работает с ранее выделенной памятью и старыми строками. Добавление новых строк и выделение памяти осуществляется посредством функций calloc или malloc.

Читайте также: