Автор Анна Евкова
Преподаватель который помогает студентам и школьникам в учёбе.

Особенности и примеры использования массивов при разработке программ (Одномерные массивы)

Содержание:

Введение

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

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

В связи со всем изложенным выше цель данной работы – рассмотреть особенности и примеры использования массивов при разработке программ.

1. Массивы

Массивы - это группа элементов одинакового типа (double, float, int и т.п.), хранящихся под одним именем. Массив характеризуется своим именем, типом хранимых элементов, размером (количеством элементов), нумерацией элементов и размерностью. Различают одномерные и многомерные массивы. Основная форма объявления массива размерности N имеет следующий формат: тип < имя массива> [размер 1][размер2]... [размер N ];
тип - базовый тип элементов массива,
[размер1][размер2]... [ размер N] - количество элементов одномерных массивов, входящих в многомерный массив.


1.1 Одномерные массивы


Чаще всего используются одномерные массивы, форма объявления которых будет иметь вид Тип [размер]. Например, оператор int A[10]; объявляет массив с именем А, содержащий 10 целых чисел. Доступ к элементам массива осуществляется выражением А[i] , где i - индекс элементов массива, который начинается с нуля и в данном примере заканчивается цифрой 9. Поэтому элемент А[0] характеризует значение первого элемента массива, А[1] - второго, А[9] - последнего. Объявление массива можно совмещать с заданием элементам массива начальных значений. Эти значения перечисляются в списке инициализации после знака равенства, разделяются запятыми и заключаются в фигурные скобки, например: int A[10] = {1,2,3,4,5,6,7,8,9,10};. Элементы массива могут иметь любой 89 тип. Так, например, оператор char S[10]; объявляет массив из символов. Массив символов - это фактически строка, и число символов, помещаемых в строку, должно быть на единицу меньше объявленного размера массива. Это обусловлено тем, что строка кончается нулевым символом и будет, к примеру, иметь вид char S[10] = {"abcdefghi\0"};. Нулевой символ в конце можно не указывать, поэтому нормально будет воспринято такое объявление: char S[10] = {"abcdefghi"};


1.2 Многомерные массивы


Многомерным называется массив, элементами которого являются одномерные массивы. Например, двумерный массив может быть объявлен таким образом: int A2[10][3];. Этот оператор описывает двумерный массив, который можно представить себе как таблицу, состоящую из 10 строк и 3 столбцов. Доступ к значениям элементов многомерного массива обеспечивается через индексы, каждый из которых заключается в квадратные скобки. Например, A2[3][2] - значение элемента, лежащего на пересечении четвёртой строки и третьего столбца (напоминаем, что индексы начинаются с 0). Если многомерный массив инициализируется при его объявлении, список значений по каждой размерности заключается в фигурные скобки. Приведённый ниже оператор объявляет и инициализирует двумерный массив A2 размерностью 3 на 5: int A2[3][5] = {{1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15}};, однако допустим и упрощенный способ инициализации: int A2[3][5] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};.



1.3 Функции генерации массивов


PascalABC.NET обладает богатым набором функций, позволяющих формировать массивы с нужным содержимым. Благодаря этим функциям во многих случаях при определении массива можно обойтись не только без указания его типа, но и без вызова конструктора.
Имеются две основные функции генерации массивов: ArrFill и ArrGen, причем вторая функция имеет несколько перегруженных вариантов, отличающихся своими параметрами. Во всех функциях генерации массива первым параметром является его размер count типа integer. Вторым параметром функции ArrFill является значение, которое присваивается всем элементам созданного массива. Этот параметр может иметь произвольный тип; именно по типу второго параметра определяется тип элементов массива, т. е. тип возвращаемого значения функции ArrFill.
Подобное «гибкое» поведение функции ArrFill возможно благодаря тому, что эта функция (как и практически все подпрограммы, связанные с обработкой массивов, и очень многие другие подпрограммы языка PascalABC.NET) является обобщенной функцией. Обобщенные подпрограммы позволяют использовать в качестве одного или нескольких типов имена-«заменители» (обычно T или T1, T2, и т. д.), означающие любой возможный тип. Имена-заменители должны указываться в заголовке подпрограммы в угловых скобках сразу после ее имени и в дальнейшем могут применяться, как и «обычные» имена типов, при описании параметров, возвращаемого значения функции и локальных переменных. Например, заголовок функции ArrFill выглядит следующим образом:
function ArrFill(count: integer; x: T): array of T
С помощью этой функции можно очень легко определить массив, состоящий, например, из 10 целых чисел, равных 1:
var a := ArrFill(10, 1);
Если в качестве второго параметра указать 1.0, то будет создан массив вещественных чисел того же размера и с тем же значением.
Гораздо больше возможностей по созданию массивов предоставляют различные варианты функции ArrGen. Во всех этих вариантах ключевую роль играют параметры — лямбда-выражения, которые подробно обсуждались в п. 1.3. Приведем заголовки для всех вариантов функции ArrGen (здесь и далее в полужирных квадратных скобках будут указываться необязательные параметры, для которых предусмотрены значения по умолчанию; кроме того, для упрощения записи мы не будем в дальнейшем указывать текст после имени обобщенной подпрограммы):
function ArrGen(count: integer; f: integer -> T
[; from: integer]): array of T
function ArrGen(count: integer; first: T; next: T -> T): array of T
function ArrGen(count: integer; first,second: T;
next: (T,T) -> T): array of T
Как уже было отмечено, первый параметр определяет размер массива. Если вторым параметром является лямбда-выражение f типа integer -> T, то в массив последовательно записываются значения f(i), начиная от i, равного параметру from. Если параметр from не указан, то он полагается равным 0; в этом случае значение f(i) записывается в элемент массива с индексом i. Данный вариант функции ArrGen удобно использовать, если имеется явная формула, определяющая значение элемента по его индексу.
В качестве примера использования данного варианта функции ArrGen приведем решение задачи Array1, в которой требуется сформировать массив, состоящий из n первых положительных нечетных чисел (1, 3, 5, …):
Task('Array1'); // Вариант 1
var a := ArrGen(ReadInteger, i -> 2 * i + 1);
a.Write;
Для вывода полученного массива мы использовали вспомогательный метод Write из модуля PT4. Поскольку метод Write можно применить к массиву сразу после его создания, в решении можно обойтись без переменной a:
Task('Array1'); // Вариант 1a
ArrGen(ReadInteger, i -> 2 * i + 1).Write;
Разумеется после выполнения данного оператора мы потеряем связь с созданным массивом, однако на правильность решения это не повлияет.
Обратимся к другим вариантам функции ArrGen. Эти варианты удобны в ситуациях, когда для определения значений элементов массива используются не явные, а рекуррентные формулы, т. е. выражения, в которых значение очередного элемента определяется через значение одного или нескольких предыдущих элементов. При этом надо явно определить значения соответствующего количества начальных элементов массива. Функция ArrGen позволяет использовать рекуррентные соотношения с одним или двумя параметрами. Иными словами, с ее помощью можно создавать массивы, в которых каждый последующий элемент определяется по одному или двум предыдущим элементам. Рекуррентная формула указывается в виде лямбда-выражения next и является последним параметром функции. Перед ней указывается один или два параметра, задающие значения начальных элементов массива.
Используя рекуррентное соотношение, мы можем запрограммировать решение задачи Array1, не содержащее операции умножения:
Task('Array1'); // Вариант 2
ArrGen(ReadInteger, 1, e -> e + 2).Print;
Классическим примером последовательности, в которой элемент определяется по двум предыдущим, является последовательность Фибоначчи. В ней два первых элемента равны 1, а следующие равны сумме двух предыдущих: 1, 1, 2, 3, 5, 8, 13, 21, 34 и т. д. В задаче Array5 требуется сформировать и вывести массив, содержащий n первых чисел Фибоначчи. С использованием последнего варианта функции ArrGen эта задача также решается в одну строку:
Task('Array5'); // Вариант 1
ArrGen(ReadInteger, 1, 1, (e1, e2) -> e1 + e2).Print;
При программировании рекуррентной формулы с двумя параметрами надо учитывать, что ближайшим соседом к формируемому элементу считается второй параметр e2 лямбда-выражения; иными словами, если через е3 обозначить значение вычисляемого элемента, то порядок значений в массиве будет следующим: e1, e2, e3.
Завершая обзор средств формирования массивов, отметим две дополнительные возможности. Во-первых, в массив можно превратить любой набор однотипных значений, указав эти значения в качестве параметров функции Arr (количество параметров может быть произвольным):
var a := Arr(1, 10, 2, 9, 3, 8);
целых чисел (располагающихся в массиве в том же порядке). Вызов Arr(x) формирует одноэлементный массив, единственный элемент которого имеет значение x (в некоторых ситуациях требуется использовать одноэлементные массивы, и указанный вызов является простейшим способом создать такой массив). Функция Arr входит в группу так называемых коротких функций (названных так из-за краткости их имен), позволяющих быстро сформировать различные структуры с требуемыми значениями. Другие примеры коротких функций приводятся в п. 3.4 и 4.6.
Второй возможностью, оказывающейся полезной, в частности, при тестировании программ, является создание числовых массивов, заполненных случайными значениями. Для этого предназначены функции ArrRandomInteger (имеет краткий синоним ArrRandom) и ArrRandomReal:
function ArrRandomInteger(n: integer := 10; a: integer := 0;
b: integer := 100): array of integer
function ArrRandomReal(n: integer := 10; a: real := 0;
b: real := 10): array of real
В каждой из этих функций можно указывать от 0 до 3 необязательных параметров. Первый параметр n, как и в случае функций ArrFill и ArrGen, задает размер массива (по умолчанию создается массив из 10 элементов), второй и третий параметры a и b задают диапазон, из которого выбираются случайным образом значения элементов. В случае целых чисел в диапазон 36 входят все целые числа от a до b включительно, по умолчанию используется диапазон от 0 до 100; в случае вещественных чисел диапазон представляет собой полуинтервал [a, b), т. е. левый конец диапазона включается, а правый нет, по умолчанию используется полуинтервал [0; 10). В случае вещественных чисел при a ≠ b можно считать, что случайные числа выбираются из интервала (a, b), так как вероятность случайного выбора числа, равного a, практически равна нулю.
Интересно отметить, что параметр a может быть больше параметра b, границы диапазона при этом будут установлены правильно. Эта особенность справедлива и для полезной функции Random(a, b: integer), которая возвращает случайное целое число от a до b включительно: допускается ситуация, когда a > b, в этом случае используется диапазон от b до a (напомним, что имеется также вариант функции Random без параметров, возвращающий случайное вещественное число из полуинтервала [0; 1)). Отмеченную особенность надо учитывать при указании в функциях ArrRandomInteger и ArrRandomReal только параметра a (без параметра b). Например, вызов ArrRandomInteger(20, 5), как и следовало ожидать, вернет массив из 20 элементов со случайными значениями в диапазоне от 5 до 100 (значение b = 100 используется по умолчанию). Если же выполнить вызов ArrRandomInteger(20, 150), то мы получим массив из 20 элементов со случайными значениями в диапазоне от 100 до 150 (значение b по-прежнему считается равным 100, поэтому границы a = 150 и b = 100 «меняются местами»). Аналогично будет вести себя и функция ArrRandomReal, вызванная с двумя параметрами n и a.

2. Особенности присваивания статических и динамических
массивов


При работе с динамическими массивами необходимо учитывать, что они являются ссылочными типами данных, как и большинство типов в PascalABC.NET5 . Это означает, что переменная, описанная как динамический массив, фактически содержит только адрес того участка памяти, который выделен для хранения элементов массива и дополнительной информации о нем (в эту информацию входит, например, размер массива). Именно поэтому подобную переменную можно описать еще до того, как память для массива будет выделена:
var a: array of integer;
В результате такого описания переменная a будет содержать значение nil (пустой адрес), означающее, что с ней еще не связан никакой «реальный» массив. Только инициализация переменной a с помощью конструкции new integer[10] (или другим способом, например с применением функции-генератора) обеспечивает ее связь с существующим массивом. В противоположность этому описание переменной как статического массива (например, var s: array[1..10] of integer) сразу выделяет для этого массива память (размер которой определяется указанным диапазоном индексов) и связывает всю эту память с описываемой переменной.
Отмеченные различия существенным образом влияют на смысл операции присваивания, выполняемой для статических и динамических массивов.
Для статических массивов присваивание вида a := b возможно только в случае, если переменные a и b описаны с применением общего описателя массива или если для них использован один и тот же именованный тип. Например, можно описать массивы таким образом:
var a, b: array[1..10] of integer;

b := a; // допустимое присваивание
Можно описать массивы и по отдельности, но для их совместимости по присваиванию необходимо в этом случае ввести новый тип (который требуется определить в разделе описаний):
type intArray = array[1..10] of integer;
begin
var a: intArray;
var b: intArray;

b := a; // допустимое присваивание
В любом случае в результате присваивания вида a := b произойдет копирование значений всех элементов массива a в элементы массива b с теми же индексами.
Если же попытаться откомпилировать следующий фрагмент, то произойдет ошибка компиляции:
var a: array[1..10] of integer;
var b: array[1..10] of integer;
b := a; // ошибка компиляции
При этом сообщение об ошибке будет иметь следующий «странный» вид: «Нельзя преобразовать тип array [1..10] of integer к array [1..10] of integer». Подобное сообщение объясняется тем, что при каждом появлении в программе нового описания статического массива компилятор создает новый «внутренний» именованный тип, который связывает с описываемой переменной. Поскольку в нашем примере a и b описаны в разных операторах описания, для них были сгенерированы разные внутренние типы (условно говоря, arr1_10_integer1 и arr1_10_integer2), которые не считаются эквивалентными и, в частности, не являются совместимыми по присваиванию. Поэтому компилятор фактически сообщает, что он не может преобразовать тип arr1_10_integer1 к arr1_10_integer2, но поскольку эти имена являются внутренними и отсутствуют в программе, он заменяет их на текст исходных описаний (чем, возможно, только сбивает с толку неопытного программиста).
Указанная особенность, связанная с эквивалентностью типов, приводит к аналогичным проблемам при использовании статических массивов в качестве параметров подпрограмм (или возвращаемых значений функций). Поскольку мы в дальнейшем не собираемся работать со статическими массивами, мы не будем обсуждать детали этих проблем (см. по этому поводу [2, п. 13.7.1]). Упомянутые особенности и связанные с ними проблемы являются добавочным аргументом в пользу того, чтобы отказаться от использования статических массивов в программах на языке PascalABC.NET.
С динамическими массивами ситуация совершенно иная. Для эквивалентности динамических массивов (и, следовательно, их совместимости по присваиванию) достаточно, чтобы они имели одинаковый тип элементов. Где и как они описаны в программе, не имеет никакого значения. Подобная эквивалентность называется структурной (в отличие от именной эквивалентности статических массивов). Заметим, что структурная эквивалентность в PascalABC.NET реализована также для множеств set of T [1, гл. 3] и процедурных типов (см. п. 1.3).
Поскольку размер динамического массива при описании не указывается, он не влияет на совместимость массивов. Но при таком подходе поэлементное копирование при присваивании теряет смысл. И действительно, присваивание динамических массивов означает совсем другое: оно лишь изменяет ссылку, хранящуюся в изменяемой переменной-массиве. Приведем пример:
var a := Arr(1, 2, 3);
var b := Arr(6, 7);
b := a;
Поскольку в генераторах Arr для переменных a и b использованы параметры одинакового типа (integer), эти переменные имеют одинаковый тип array of integer и поэтому совместимы по присваиванию. Подчеркнем, что с ними связываются массивы разного размера. Фактически в a хранится ссылка на массив с элементами 1, 2, 3, а в b хранится ссылка на массив с элементами 6 и 7. Присваивание вида b := a изменяет лишь ссылку, хранящуюся в переменной b. Это приводит к важным последствиям. Во-первых, программа теряет связь с массивом, содержащим элементы 6, 7, поскольку эта связь обеспечивалась только за счет ссылки, хранящейся в b. Массив становится недоступным программе и в дальнейшем автоматически удаляется из памяти специальной подсистемой платформы .NET — сборщиком мусора (garbage collector) [3, п. 8.1.2]. Во-вторых, доступ к массиву с элементами 1, 2, 3 теперь оказывается возможным и с помощью переменной а, и с помощью переменной b.
Дополним предыдущий фрагмент следующими операторами
b[0] := 5;
Write(a); // [5, 2, 3]
Мы видим, что изменение элемента b[0] привело к тому, что одновременно изменился и массив a (так как фактически у нас имеется единственный массив с двумя именами).
Указанное обстоятельство необходимо учитывать при передаче динамических массивов в качестве параметров подпрограмм. В частности, следует понимать, что и передача динамического массива по значению, и передача его по ссылке (с указанием модификатора var) будет, во-первых, выполняться одинаково быстро (так как передача по значению не будет приводить к поэлементному копированию) и, во-вторых, позволит изменять элементы переданного массива (даже при передаче массива по значению, поскольку созданная в подпрограмме при такой передаче копия ссылки будет обращаться к тому же самому массиву).
Как при подобном способе присваивания создать настоящую копию массива, которую в дальнейшем можно было бы обрабатывать независимо? Для этого в PascalABC.NET предусмотрено несколько способов.
Простейшим является использование функции Copy(a) с единственным параметром — копируемым массивом. Эта функция создает и возвращает копию данного массива (точнее, возвращается ссылка на созданную в памяти копию). Пример:
var a := Arr(1, 2, 3);
var b := Arr(6, 7);
b := Copy(a);
b[0] := 5;
Writeln(a); // [1, 2, 3]
Writeln(b); // [5, 2, 3]
Мы видим, что теперь изменение значения элемента массива b никак не повлияло на содержимое массива a.
Заметим, что для получения копий массива или его частей можно использовать срезы (см. п. 3.7), поскольку любой срез фактически является копией некоторой части массива. Например, в предыдущем фрагменте мы могли вместо оператора присваивания с функцией Copy использовать вариант со срезом:
b := a[:];
Важно понимать, что при описанных способах копирования выделяется дополнительная память для хранения копии.
Копию можно создать без выделения памяти, если скопировать данные из одного массива (массива-источника) непосредственно в другой (массив-приемник). Для этого можно использовать процедуру (метод массива) CopyTo, вызов которой имеет вид src.CopyTo(dst, dstStart), где src определяет массив-источник (от англ. source), dst — массив-приемник (от англ. destination), а dstStart — индекс в массиве dst, начиная с которого в него будут записываться элементы массива src. Всегда копируются все элементы массива-источника; если для этого действия в массиве-приемнике недостаточно элементов, то будет возбуждено исключение.
Например, если массивы a и b инициализированы так же, как в предыдущем примере, то вызов a.CopyTo(b, 0) приведет к аварийному завершению программы, а вызов b.CopyTo(a, 0) выполнится успешно (в результате в a будут содержаться три элемента: 6, 7, 3). Успешно проработает и вызов b.CopyTo(a, 1); в этом случае в a будут содержаться элементы 1, 6, 7.
С помощью метода CopyTo можно реализовать короткий и эффективный алгоритм объединения массивов, не использующий циклы. Предположим, что требуется объединить элементы массивов a и b (в указанном порядке) в новом массиве res. Вначале надо выделить память для массива res (при этом его размер должен быть равен суммарному размеру исходных массивов), а затем вызвать процедуру CopyTo дважды: для копирования в нужные позиции массива res элементов массива a и b, например:
var res := new integer[a.Length + b.Length];
a.CopyTo(res, 0);
b.CopyTo(res, a.Length);
В языке PascalABC.NET предусмотрен специальный вариант операции + для динамических массивов, позволяющий для формирования массива res использовать вместо указанных выше операторов единственный оператор:
var res := a + b;
Операция a + b размещает в памяти массив и заполняет его нужным образом, после чего ссылка на эту область памяти записывается в переменную res. Обратите внимание на то, что в данном случае тип массива res выводится из типов массивов a и b; необходимо лишь, чтобы типы массивов a и b совпадали.
Проиллюстрируем применение рассмотренных возможностей, решив задачу Array57. В ней требуется записать в новый целочисленный массив b вначале все элементы исходного массива a с четными порядковыми номерами (2, 4, 6, …), а затем — с нечетными (1, 3, 5, …).
При решении надо учесть, что индекс элемента на 1 меньше порядкового номера, поэтому вначале надо записать в массив b все элементы массива a с нечетными индексами, а затем — с четными:
Task('Array57');
// Вариант 1 var a := ReadArrInteger;
var b := a[1::2] + a[0::2];
b.Print;
Мы могли бы не связывать полученный массив с переменной b, а сразу вывести его:
Task('Array57');
// Вариант 1a var a := ReadArrInteger;
(a[1::2] + a[0::2]).Print;
Это не нарушает условия задачи, так как в данном случае мы также сформировали новый массив (просто при этом мы не связали созданный массив с каким-либо именем).
Для сравнения приведем решение, использующее цикл:
Task('Array57'); // Вариант 2
var a := ReadArrInteger;
var b := new integer[a.Length];
for var i := 0 to a.Length div 2 - 1 do
b[i] := a[2 * i + 1];
for var i := 0 to (a.Length + 1) div 2 - 1 do
b[a.Length div 2 + i] := a[2 * i];
b.Print;
Разумеется, по части наглядности и простоты реализации этот вариант не выдерживает сравнения с предыдущим. Правда, он более эффективно использует память: кроме исходного массива a память выделяется только для результирующего массива b. В первом варианте память выделялась для массива a, для каждого из двух срезов, а также для суммы этих срезов (ссылка на которую записывалась в b). Если считать, что массив a содержит N элементов, то во втором варианте программы выделялась память для хранения 2N элементов (массивы a и b), а в первом — для хранения 3N элементов (массив a, два среза и их сумма). Однако в большинстве ситуаций подобный «перерасход» памяти (и связанное с ним небольшое замедление алгоритма) можно не принимать во внимание.
Замечание. Во втором варианте решения были использованы формулы, позволяющие определить количество элементов с нечетными и четными индексами для массива размера N. Для элементов с нечетными индексами формула имеет вид N div 2, а для элементов с четными индексами — (N + 1) div 2. При этом число N может быть как четным, так и нечетным.


2.1 Изменение размеров динамического массива


В определении массива, данном в п. 3.1, особо отмечалось, что массив размещается на непрерывном участке оперативной памяти. Это условие является важным, так как оно обеспечивает быстрое нахождение адреса любого элемента массива по его индексу. С другой стороны, отмеченное условие, казалось бы, не позволяет изменять размер уже созданного массива. Действительно, после участка, на котором располагается массив, могут быть размещены другие данные программы, и поэтому «расширить» память для массива (сохраняя ее в виде непрерывного участка) окажется невозможно. Это справедливо для статических массивов, которые, будучи созданы, сохраняют свой размер на протяжении всей программы. Однако для динамических массивов ситуация иная.
Во всех современных реализациях Паскаля предусмотрены средства, позволяющие изменить размер динамического массива (эти средства были добавлены в язык одновременно с включением в него динамических массивов; из распространенных реализаций Паскаля это впервые было сделано в языке Delphi Pascal версии 4). Таким образом, слово «динамический» в отношении массивов PascalABC.NET означает не только то, что массив можно создать в любой момент выполнения программы, но и то, что в дальнейшем его размер можно изменить с сохранением прежнего содержимого.
Для изменения размера массива a достаточно вызвать процедуру SetLength(a, newLength), в которой второй параметр определяет новый размер. В результате переменная a будет связана с массивом нового размера (который может быть больше или меньше размера исходного массива), причем все сохранившиеся элементы исходного массива сохранят свои значения (а новые будут содержать нулевые значения соответствующего типа).
Следует, однако, иметь в виду, что операция изменения размера динамического массива является длительной и ресурсоемкой операцией. Она требует, во-первых, выделения памяти под новый массив и, во-вторых, копирования в новый массив сохраняемой части исходного массива.
Заметим, что процедуру SetLength можно вызвать даже для неинициализированного массива, т. е. сразу после его описания (когда переменная a имеет значение nil и, таким образом, не связана с каким-либо конкретным массивом, размещенным в памяти). Это означает, что процедуру SetLength можно использовать для инициализации массива, как и конструктор new. Именно с помощью процедуры SetLength обеспечивается инициализация динамических массивов в языке Delphi Pascal, тогда как вариант инициализации с применением конструктора new заимствован языком PascalABC.NET из языка C#. Вариант с конструктором является более кратким, так как не требует предварительного описания массива. Ниже приведены два варианта описания и инициализации массивов одинакового размера: для массива a1 использован конструктор, для мас
сива a2 — процедура SetLength:
var a1 := new integer[10];
var a2: array of integer;
SetLength(a2, 10);
Чтобы проиллюстрировать особенности применения процедуры SetLength, рассмотрим задачу Array54, в которой требуется переписать в новый массив b все четные числа из исходного массива a в том же порядке и вывести размер полученного массива и его элементы. Одним из вариантов решения может быть следующий:
Task('Array54'); // Вариант 1
var a := ReadArrInteger;
var b := new integer[0];
foreach var e in a do
if not Odd(e) then
begin
SetLength(b, b.Length + 1);
b[b.Length - 1] := e;
end;
b.WriteAll;
Вначале мы создаем пустой результирующий массив b, а затем в цикле, как только в исходном массиве обнаруживается четное число, увеличиваем размер массива b и добавляем это число в конец увеличенного массива. Для вывода как размера массива, так и его элементов мы использовали специальный метод вывода WriteAll, предусмотренный в задачнике. Заметим, что создать пустой массив целого типа можно было бы и с помощью функции-генератора ArrFill(0, 0).
Это решение дает правильный результат и выглядит достаточно привлекательно. Однако многократные вызовы процедуры SetLength при обработке массивов большого размера могут существенно замедлить выполнение этого алгоритма. Для того чтобы это продемонстрировать, будем генерировать большие массивы случайных чисел (используя функцию ArrRandom — см. п. 3.3), применять к ним описанный алгоритм и замерять время его работы. Время генерации исходного массива не будем учитывать, не будем также выводить на печать полученный массив:
var size := 10000;
for var k := 1 to 6 do
begin
size *= 2;
var a := ArrRandom(size);
MillisecondsDelta;
// начало алгоритма
var b := new integer[0];
foreach var e in a do
if not Odd(e) then
begin
SetLength(b, b.Length + 1);
b[b.Length - 1] := e;
end;
// конец алгоритма
WritelnFormat('size = {0,6} time = {1,6}', size,
MillisecondsDelta);
end;
В данном случае для измерения времени использовалась функция MillisecondsDelta. Ее отличие от уже известной нам функции Milliseconds (см. п. 3.4) состоит в том, что функция MillisecondsDelta возвращает время (в миллисекундах), прошедшее с момента последнего вызова этой же функции (при первом вызове функции MillisecondsDelta, как и при вызове Milliseconds, возвращается время, прошедшее с момента начала работы программы). Обратите внимание на то, что первый из двух вызовов функции MillisecondsDelta имеет вид вызова процедуры, поскольку в данном случае нам не требуется возвращаемое значение: мы лишь «помечаем» начало того участка программы, время работы которого требуется измерить.
Полужирным шрифтом выделен фрагмент, для которого подсчитывается время работы. Приведем возможный результат выполнения программы (напомним, что программу следует запускать в режиме релиза — см. замечание в п. 3.4):
size = 20000 time = 36
size = 40000 time = 147
size = 80000 time = 1483
size = 160000 time = 8682
size = 320000 time = 39110
size = 640000 time = 147069

Мы видим что, время растет с большей скоростью, чем размер size.
Алгоритм можно записать короче, если использовать операцию + для массивов:
Task('Array54'); // Вариант 2
var a := ReadArrInteger;
var b := new integer[0];
foreach var e in a do
if not Odd(e) then
b := b + Arr(e);
b.WriteAll;
В этом решении мы в цикле добавляем к массиву b одноэлементный массив, содержащий элемент e. Быстродействие у данного варианта решения будет таким же, как и у предыдущего.
Теперь рассмотрим вариант решения, не требующий многократного выделения памяти и копирования элементов:
Task('Array54'); // Вариант 3
var a := ReadArrInteger;
var b := new integer[a.Length];
var m := 0; foreach var e in a do
if not Odd(e) then
begin
b[m] := e;
m += 1;
end;
SetLength(b, m);
b.WriteAll;
В этом варианте для массива b сразу выделяется наибольший возможный размер (совпадающий с размером исходного массива). Кроме того, вводится переменная m, определяющая «заполненность» массива b реальными элементами, полученными из a. В начале алгоритма заполненность равна 0. В цикле четные элементы добавляются в конец заполненной части массива и одновременно увеличивается m. После завершения цикла останется уменьшить размер массива b, оставив в нем только заполненную часть. Таким образом, в этом варианте процедура SetLength вызывается только один раз.
Замечание. Можно было бы вообще не вызывать в этой программе процедуру SetLength, но тогда в массиве b останутся «лишние» нулевые элементы, что не позволит использовать для его вывода метод WriteAll (поскольку этот метод выведет все элементы, в том числе и «лишние»). В этой ситуации можно было бы вывести в качестве размера число m, а для вывода элементов использовать цикл for. Если же полученный таким способом массив предполагается использовать в других частях программы, то вызов процедуры SetLength существенно упростит его дальнейшее использование, поскольку в противном случае значение свойства Length для этого массива не будет соответствовать размеру его «истинного» содержимого.
Тестирование нового варианта алгоритма для массивов большого размера дает следующий результат:
size = 20000 time = 1
size = 40000 time = 1
size = 80000 time = 1
size = 160000 time = 1
size = 320000 time = 3
size = 640000 time = 6

Мы получили гораздо более эффективный алгоритм.
Приведем еще один вариант решения, использующий изученные нами в предыдущей главе запросы. В данном случае достаточно применить запрос фильтрации Where (см. п. 4.2), отбирающий нужные элементы. Чтобы полученную последовательность можно было сохранить в виде массива (как того требуют условия задачи), достаточно применить к ней экспортирующий запрос ToArray, рассмотренный в п. 4.6:
Task('Array54'); // Вариант 4
var a := ReadArrInteger;
var b := a.Where(e -> not Odd(e)).ToArray;
b.WriteAll;
Для проверки быстродействия этого варианта в программе замера времени между комментариями «начало алгоритма» и «конец алгоритма» надо поместить единственный оператор:
var b := a.Where(e -> not Odd(e)).ToArray;
Проверка быстродействия показывает, что оно будет почти таким же, как и у варианта 3 (использующего «счетчик заполненности» m):
size = 20000 time = 3
size = 40000 time = 1
size = 80000 time = 1
size = 160000 time = 2
size = 320000 time = 5
size = 640000 time = 10

Конечно, в отношении наглядности вариант со счетчиком заполненности существенно проигрывает варианту, использующему запросы.
Если немного отступить от условий задачи, то в последнем варианте решения можно обойтись и без запроса ToArray. В этом случае в переменной b будет сохраняться ссылка не на массив, а на последовательность, содержащую требуемый набор данных:
Task('Array54'); // Вариант 4a
var a := ReadArrInteger;
var b := a.Where(e -> not Odd(e));
b.WriteAll;
Данное решение также будет засчитано как правильное. Однако нам не удастся проверить его быстродействие, подставив в программу замера времени оператор
var b := a.Where(e -> not Odd(e));
При запуске этого варианта мы получим следующий результат:

size = 20000 time = 3
size = 40000 time = 0
size = 80000 time = 0
size = 160000 time = 0
size = 320000 time = 0
size = 640000 time = 0

Разумеется, это не означает, что в данном варианте алгоритм работает менее 1 миллисекунды для исходных массивов любого размера (большего 20000). Вспомним, что основная особенность запросов, преобразующих последовательности, состоит в их отложенном выполнении. Таким образом, в варианте 4a при формировании последовательности b не выполняется фактическая обработка исходного массива a: в последовательность b лишь записывается «правило», по которому эта обработка будет происходить впоследствии, когда, например, элементы последовательности будут перебираться в цикле foreach или когда последовательность будет преобразована в массив запросом ToArray (как в варианте 4). Понятно, что для записи правила обработки требуется очень мало времени и, главное, это время не зависит от размера обрабатываемых массивов.
Замечание. Значение 2, выведенное в первой строке, связано с выполнением некоторых дополнительных действий программы при первом обращении к запросу Where. Если, например, вызвать запрос Where в начале программы (применив его к произвольной последовательности и указав произвольный предикат), то в выведенном списке все значения time будут равны нулю.



3. Основные действия с массивами


Единственное действие, которое можно выполнять над массивами целиком, причем только при условии, что массивы однотипны, – это присваивание. Если в программе описаны две переменные одного типа, например:
Var
a, b : array [1..10] of real;
то можно переменной a присвоить значение переменной b (a:=b). При этом каждому элементу массива a будет присвоено соответствующее значение из массива b. Все остальные действия над массивами Паскаля производятся поэлементно (это важно!).
Ввод Массива
Для того чтобы ввести значения элементов массива, необходимо последовательно изменять значение индекса, начиная с первого до последнего, и вводить соответствующий элемент. Для реализации этих действий удобно использовать цикл с заданным числом повторений, где параметром цикла будет выступать переменная – индекс массива. Значения элементов могут быть введены с клавиатуры или определены с помощью оператора присваивания.
Пример фрагмента программы ввода массива с клавиатуры:
Var
A : array [1..10] of integer;
i : byte; {переменная i как индекс массива}
Begin
For i:=1 to 10 do
Read (a[i]); { ввод i- го элемента производится с клавиатуры }

Рассмотрим теперь случай, когда массив заполняется автоматически случайными числами. Для этого будем использовать генератор случайных чисел – random ( N ).
Пример фрагмента программы заполнения массива случайными числами:
Var
A: array [1..10] of integer;
i : byte ;
Begin
For i :=1 to 10 do
A [ i ]:= random (50)-25; {i-му элементу массива
присваивается «случайное» целое
число в диапазоне от 0 до 49 с
вычетом 25, т.е. окончательный
диапазон от -25 до 24} .

Вывод массива
Вывод массива в Паскале осуществляется также поэлементно, в цикле, где параметром выступает индекс массива, принимая последовательно все значения от первого до последнего.
Пример фрагмента программы вывода массива:
Var
A: array [1..10] of integer;
i : byte ; {переменная i как индекс массива}
Begin
Writeln(‘Массив А’);
For i :=1 to 10 do
Write ( a [ i ]:5); {вывод массива осуществляется в строку,
под каждый элемент выделяется 5
позиций, иначе элементы массива будут
выведены слитно} .
Writeln; (Для перевода курсора на следующую строку)
На экране мы увидим, к примеру, следующие значения:
Массив А
_ _ _ _ 5_ _ _ - 2_ _ 1 1 5 и т.д.
Вывод можно осуществить и в столбик (использовать оператор Writeln). Но в таком случае нужно учитывать, что при большой размерности массива все элементы могут не поместиться на экране и будет происходить скроллинг, т.е. при заполнении всех строк экрана будет печататься очередной элемент, а верхний смещаться за пределы экрана.
Пример программы вывода массива в столбик:
Var
A: array [1..10] of integer;
i : byte ;
Begin
For i:=1 to 10 do
Writeln (‘a[‘, i,’]=’, a[i]); {вывод элементов массива в столбик} .


На экране мы увидим, к примеру, следующие значения:
a [1]=2
a [2]=4
a [3]=1 и т.д.



 

4. Примеры использования массивов



Так как массив является структурным типом, то основные операции с ним необходимо проводить с помощью оператора цикла. Покажем это на ряде примеров. В примере на рис. 11.1 с помощью оператора цикла осуществляется присвоение начальных нулевых значений элементам массива n[5], содержащего пять целых чисел. Печать массива осуществляется в табулированном формате. Первый оператор вывода печатает на экране заголовки столбцов, а второй выводит эле- 90 менты массива и их значения. Функция setw() указывает ширину поля, в котором будет выведено следующее значение.
#include
#include
#include
#include
main( )
{ clrscr();
// очистка экрана
int n[5];
for(int i=0;i<5;i++)n[i]=0;
// обнуление элементов массива
cout<<"элемент"«setw(13)<<"значение\n"; // вывод заголовков
for(i=0;i<5;i++)
cout<<setw(7)<<i<<setw(13)<<n[i]<<"\n";
// вывод элементов
cout<<"\nНажмите любую клавишу…";
getch();
return 0;

}__________________________________________________________

Результаты работы программы:
элемент значение
0 0
1 0
2 0
3 0
4 0
Рис. 11.1
В примере на рис. 11.2 реализуются операция копирования массивов (mes, mcopy) и вывод их элементов на экран дисплея. При копировании элементам массива mcopy последовательно (в цикле) присваиваются значения элементов массива mes. При этом для доступа к элементам массива используются индексы. Вывод элементов массива также осуществляется в цикле.
#include
#include
#include
#include
main()
{
clrscr(); int i;
int mes[12]={31,28,31,30,31,30,31,31,30,31,30,31};
// инициализация массива
int mcopy[12];
for(i=0;i<12;i++) mcopy[i]=mes[i];
// копирование массива
cout<<"Исходный массив "<<setw(29)<<" Скопированный массив"; //вывод
//заголовков
for(i=0;i<<"\nmes="<<mes[i]<<"\t\t\tmcopy="<mcopy[i]; //
вывод
// массивов
Cout<<"\nНажмите любую клавишу…";
getch();
return 0;
}_________________________________________________________
Результаты работы программы:
Исходный массив Скопированный массив
mes=31 mcopy=31
mes=28 mcopy=28

… …
mes=31 mcopy=31

Рис.11.2

На рис. 11.3 приведен пример вычисления одномерного массива y[10] по заданному массиву x[10], элементы, которых связаны следующим соотношением: y[i] = (x[i]+a)/sqrt((x[i]2+1 ));.
#include
#include
#include
main()
{
float a=0;
float y[10], x[10];

int i=0;
clrscr(); //
очистка экрана
cout<<"\nВведите a : ";
cin>>a;
//ввод переменной а
for( i=0; i<10;i++)
{
cout<<"\n
Введите x["<<i<<"] : ";
cin>>x[i]; //
ввод массива x[i]
y[i]= (x[i] + a) / sqrt((x[i]*x[i])+1);
cout<<"\t\t\t y["<<i<<"] ="<< y[i] ; //
вывод массива y[i]
}
cout<<"
Нажмите любую клавишу…";
getch();
}

Рис. 11.3
В данной программе ввод переменной а и элементов массива x[i] организован в режиме диалога.
В примере на рис. 11.4 аналогичная задача решается для двумерных массивов. В этой программе ввод и вывод массивов осуществляется с помощью вложенных циклов.
#include
#include
#include
main()
{

float a=0;
float y[3][2], x[3][2];
int i=0, j=0;
clrscr();
cout<<"\n
Введите a : ";
cin>> a; //
ввод переменной а
for( i=0; i<3; i++)
{
for( j=0; j<2; j++)
{
cout<<"\nВведите x["<<i<<","<<j<<"]:" ;

cin>> x[i][j]; // ввод элементов массива x[i][j]
y[i][j]=(x[i][j] + a)/sqrt((x[i][j] * x[i][j])+1);
cout<<"\t\t\t y["<<i<<","<<j<<"]="<<y[i][j]; // вывод элементов
массива
}
cout<<"\n"; //вывод пустой строки
cout<<"\n\n";
cout<<"\n
Нажмите любую клавишу ...";
getch();
return 0;
}

Рис. 11.4.
На рис. 11.5 приведен пример программы, в которой вычисляется количество положительных, отрицательных и нулевых элементов одномерного массива.
Количество положительных элементов суммируется в переменной p, отрицательных - в переменной n, а нулевых - в переменной zero.
#include
#include
#include
main()
{
float a[10];
int i=0, n=0, p=0, zero=0; //
обнуление переменных
clrscr();
cout<<"\
nОпределить количество положительных и отрицательных
элементов массива a[10]\n";
for( i=0; i<10;i++)
{
cout«"\nВведите a["<<i+1<<"] : ";
cin>>a[i];
for( i=0; i<10; i++)
{
if( a[i] > 0) p += 1; //
определение количества положительных .элементов
if( a[i] < 0) n += 1; // определение количества отрицательных элементов
if( a[i] == 0) zero += 1; // определение количества нулевых элементов
}
cout<<"\n\n";
cout<<"\n
Число положительных элементов ="<<p;
cout<<"\n
Число отрицательных элементов ="<<n;
cout<<"\n
Число нулевых элементов ="<<zero;
cout<<"\n\n";
cout<<"\n
Нажмите любую клавишу…";
getch();
}

Рис. 11.5.

Для задания размера массива часто удобно использовать константы. В примере на рис. 11.6 число строк двумерного массива задается константой row, а число столбцов - константой col. Использование констант для задания размера массивов делает программу более наглядной и масштабируемой, так как при любом изменении размеров массива в программе достаточно будет изменить только значения констант. Этот прием наиболее эффективен в больших программах. В данной 94 программе решается задача вычисления количества положительных, отрицательных и нулевых элементов двумерного массива размерностью 2 на 3.
#include
#include
#include
#define row 2
// строки
#define col 3 // столбцы
main()
{

float b[row][col]; // объявление массива
int i=0, j=0,n=0, p=0, zero=0;
clrscr();
cout<<"\n
Определить количество положительных и отрицательных элементов";
cout<<"\n
массива b["<<row<<","<<col<<"]\n";
for( i=0; i<row; i++){
for( j=0; j<col; j++){

cout<<"\n Введите b["«i+1«","«j+1<<"]=";
cin>> b[i][j]; // ввод элементов двумерного массива
}
cout<<"\n\n";
}
for(i=0; i<row; i++) {
for(j=0; j<col; j++) {
if(b[i][j] > 0) p += 1;
if(b[i][j] < 0) n += 1;
if(b[i][j] == 0) zero += 1;

}
}

cout<<"\nЧисло положительных элементов = "<<p;
cout<<"\n
Число отрицательных элементов = "<<n;
cout<<"\nЧисло нулевых элементов = "<<zero;
cout<<"\nНажмите любую клавишу…";
getch();
return 0;
}

Рис. 11.6.

5. Массивы и функции


При передаче массива в функцию в качестве параметра в заголовке функции необходимо указывать тип и имя массива с последующими пустыми квадратными скобками, а также тип и имя переменной, определяющей размерность массива. В 95 прототипе функции имена массива и переменной могут быть опущены. На рис. 11.7 приведена программа вычисления минимальной компоненты вектора с использованием функции vec( ), в которой заголовок функции имеет вид vec(int x[ ], int k), а аргументы int x[ ] и int k соответствуют имени массива и его размерности. В прототипе функции vec(int, int) эти имена опущены.
#include
#include
#include
#include
#define n 5
vec(int [ ], int);
// прототип функции вычисления min компоненты вектора
main()
{
int y[n], i, min;
clrscr();
for(i=0;i<n;i++){
cout<<"\n
Введите y["<<i<<"]=";
cin>>y[i]; //
ввод элементов вектора
}
min=vec(y, n); //
вызывающая функция
cout<<"\nМинимальная компонента = "<<min;
cout<<"\n
Нажмите любую клавишу…";
getch();
return 0;
}
vec(int x[ ], int k) //
заголовок функции
{
int min1=x[0];
int i;
for(i=1; i < k;i++)
if(x[i]<min1) min1=x[i];
return min1;
}

Рис. 11.7.
Для передачи многомерных массивов в функцию необходимо учитывать ряд особенностей, которые связаны с тем, что при описании функции необходимо указывать размерность второго индекса и всех последующих индексов массива. Размерность первого индекса многомерного массива также не указывается, но 96 значения размерностей всех последующих индексов необходимы. Это обусловлено тем, что в памяти компьютера элементы массивов хранятся последовательно, независимо от количества индексов (размерности массива), а знание величин индексов дает возможность компилятору сообщить функции о том, как расположены элементы в массиве. Пример программы, в которой функция mas принимает двумерный массив как параметр, приведен на рис. 11.8.
//Вычисление минимальной компоненты двумерного массива
#include
#include
#include
#include
#define n 2 //
число строк
#define m 3 // число столбцов
mas(int [ ][m], int, int); // прототип функции
main( )
{
int y[n][m], i, j, min ;
clrscr(); //
очистка экрана
for(i=0;I<n;i++)
for(j=0;j<m;j++)
{
cout<<"\n
Введите y["<<i<<"]["<<j<<"]=";
cin>>y[i][j]; // ввод массива
}
min=mas(y, n, m); //
обращение к функции и получение результата
cout<<"\n\nМинимальная компонента = "<<min;
cout<<"\nНажмите любую клавишу…";
getch( );
return 0;
}
mas(int x[ ][m], int k, int d) //
заголовок функции
{
int min1=x[0][0]; int i, j;
for( i=0; i < k; i++)
for(j=0;j<d;j++){
if( x[i][j]<min1) min1=x[i][j];
}
return min1;
}

Рис. 11.8.
В этой программе значения индексов массива задаются с помощью констант n и m, а в заголовке функция описывается следующим образом:
mas(int x[][m], int k, int d),
где m - значение второго индекса массива, а k и d - фиктивные параметры, численные значения которым присваиваются после вызова функции с помощью оператора вида mas(y, n, m);.
В прототипе функции также необходимо указывать размерность второго индекса, в соответствии с выражением mas(int [][m], int, int).



6. Массивы и указатели


Массивы и указатели в языке С++ тесно связаны и могут использоваться почти эквивалентно. Так, имя массива является константным указателем на первый элемент массива, а указатели можно использовать для выполнения любой операции, включая индексирование массива. Пусть сделано следующее объявление: int b[5] = {1,2,3,4,5}, *p;, которое означает, что объявлены массив целых чисел b[5] и указатель на целое р. Поскольку имя массива является указателем на первый элемент массива, можно задать указателю р адрес первого элемента массива с помощью оператора p = b; . Это эквивалентно присвоению адреса первого элемента массива другим способом: р = &b[0];. Теперь можно сослаться на элемент массива b[3] с помощью выражения *(р+3). В этой записи скобки необходимы, потому что приоритет * выше, чем +. На рис. 11.9 показан пример программы, которая с помощью указателей заносит данные в одномерный массив а[5]. Определяет сумму и количество положительных элементов. Выводит на экран полученный массив и адреса его элементов, а также результаты расчетов.
#include
#include
void main()
{ clrscr();
int a[5], sum = 0, *p;
int kol = 0, i;
p = &a[0]; //
инициализация указателя адресом первого элемента
cout << " Ввод данных в массив a[ ]\n";
for ( i = 0; I <5; i++){
cout << " a [ " << i << " ] = ";
cin >> *(p+i); //
разыменовывание смещенного указателя
}
// расчет суммы и количества положительных элементов
for ( i = 0; i < 5; i ++)
if ( *(p+ i) > 0 ) {
sum += *( p+i );
kol ++;
}

// вывод исходных данных и результатов
cout << "\n\n\n Элемент массива Адрес элемента массива \n";
for ( i = 0; i < 5; i++ ){
cout << *( p+ i) << "\t\t " << (p+i) << "\n"; // вывод результатов
}
cout << "\nсумма = " << sum << "^количество = " << kol; cout<<"\n\n";
cout<<"\nНажмите любую клавишу…";
getch();
}_____________________________________________________________
Результаты работы программы:

Ввод данных в массив a[ ] :
a[0]=1
a[1]=2
a[2]=5
a[3]=5
a[4]=4

Элементы массива Адрес элемента массива
1 Oxffec
2 Oxffee
3 Oxfff0
4 Oxfff2
5 Oxfff4

сумма = 17
количество=5
Рис. 11.9.
На рис.11.10 приведен пример программы, в которой с помощью указателей формируется двумерный массив а[2][2], а из минимальных элементов его столбцов формируется массив b[2]. Значения полученных массивов выводятся на дисплей.
#include
#define I 2

#define J 2
#include
void main()
{
clrscr();
int a[I][J], b[J], min, *p
; int i,j;
p = &a[0][0];
// инициализация указателя адресом первой ячейки
cout << "Введите данные в массив a["<< I <<"]["<<J<<"]:\n";
for ( i = 0; i < I; i++ )
for ( j = 0; j < J; j++ ){
cout << "a[" << i << "][" << j << "]=";
cin >> *(p + i*I + j);
// ввод массива
}
// расчет массива b[2]
for ( j = 0; j < J; j++ ) { // цикл по столбцам
min = *(p + j); // присваивание min значения первого элемента столбца
for (i = 1; i < I; i+) // цикл по строкам, начиная со второго элемента
if ( ( *(p + i*I + j)) < min) min = *(p + i*I + j);
*(b + j) = min;
}
cout <<
"nВывод исходного массива a[,]:";
for (i = 0; i < I;i++){
cout << "\n";
for ( j = 0; j < J;j++){
cout <<"\t"<< *(p + i*I + j);
}
}
cout << "\n\n
Вывод полученного массива b[]:\n";
for ( j = 0; j < J; j++ ){
cout <<"\t"<< *( b + j);
}
cout<<"\n\n";
cout<<"\n
Нажмите любую клавишу…";
getch();
}_____________________________________________________________

Результаты работы программы:
Введите данные в массив a[2][2]:
a[0][0]=1
a[0][1]=4
a[1][0]=5
a[1][1]=3
Вывод исходного массива a[2][2]:
1 4
5 3
Вывод полученного массива b[]:

1 3
Рис. 11.10.

Заключение


Мы изучили раздел «Массивы» и узнали что массив - это именованная группа однотипных данных, хранящихся в последовательных ячейках памяти. Узнали, что массивы бывают следующих видов: одномерные, двухмерные и многомерные. Чтобы составить массив необходимо задать типы его индексов и компоненты. Эти типы данных массивов позволяет одному идентификатору задать несколько значений, которые отличаются порядковым номером. Номер элемента массива указывается после идентификатора в квадратных скобках {M[5] - пятый элемент массива М}. При описании массива указывается диапазон номеров элементов массива и тип, к которому относится каждый его элемент. Единственным действием, которое возможно произвести с массивом целиком это присваивание.
Каждый элемент массива - это переменная, которой можно присваивать значения в операторах и функциях. Для того, чтобы указать элемент массива, необходимо записать все его индексы. После объявления массива каждый его элемент можно обработать, указав идентификатор (имя) массива и индекс элемента в квадратных скобках. Массив в языке Паскаль это сложный тип данных, поэтому чаще всего его описывают в разделе переменных. Массив нельзя ввести с клавиатуры одной командой, для этого организовывается цикл с параметром.

Библиография



1. Программирование и основы алгоритмизации: Для инженерных

специальностей технических университетов и вузов. /А.Г. Аузяк, Ю.А.

Богомолов, А.И. Маликов, Б.А. Старостин. Казань: Изд-во Казанского

национального исследовательского технического ун-та - КАИ, 2013, 153 с.Режим доступа: http://au.kai.ru/documents/Auzyak_Progr_osn_alg_C_2013.pdf.

2. Основы алгоритмизации и программирования : учебное пособие / Г.Р. Кадырова. – Ульяновск : УлГТУ, 2014. – 95 с. . Режим доступа:

http://venec.ulstu.ru/lib/disk/2014/137.pdf

3. Макаров В.Л. Программирование и основы алгоритмизации.: учебн.пособие.-Спб., СЗТУ, 2003, - 110с. Режим доступа:

http://window.edu.ru/resource/126/25126/files/nwpi223.pdf


4.Абрамян М.Э. Основы Программирования : учебное пособие Растов-на-Дону : Изд-во ЮФУ 2016. – 119с . Режим доступа:
http://pascalabc.net/downloads/Books/Abramyan/Abramyan-Pascal2016-1.pdf