---
course: ОАиП
lecture: 5
title: "Лекция №5. Организация данных в виде массивов."
---

# Лекция №5. Организация данных в виде массивов.

## §5.1 Определение массива

Ранее мы объявляли переменные для хранения одиночного значения типа *int*, *char* или *string*, даже если использовалось несколько их экземпляров. Однако можно объявить коллекцию объектов, например, 20 целых чисел или стаю котов.

Определение слова **массив** (*array*) в словаре довольно близко к тому, что мы хотим понять. Согласно словарю Вебстера, **массив** — это «*группа элементов, формирующих полный набор, например, массив солнечных панелей*».

**Массивы** позволяют сохранить в памяти элементы данных одинакового типа в последовательном порядке.

**Массив**

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

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

Без использования **массива** нам придется выделить почти 30 одинаковых переменных, что является очень неудобной практикой в разработке приложений.

text

```text
// Выделяем 30 целочисленных переменных
int testResultStudent1;
int testResultStudent2;
int testResultStudent3;
// ...
int testResultStudent30;
```

С использованием **массива** всё гораздо проще. Следующая строка эквивалентна коду, приведенному выше:

text

```text
int testResult[30];
```

В объявлении переменной **массива** мы используем квадратные скобки **[]**, чтобы сообщить компилятору, что это переменная массива (а не обычная переменная), а в скобках — количество выделяемых элементов (это называется *длиной* или *размером массива*).

В примере, приведенном выше, мы объявили фиксированный **массив** с именем *testResult* и длиной *30*. **Фиксированный массив** (или «\_массив фиксированной длин\_ы») представляет собой массив, размер которого известен во время компиляции. При создании *testResult*, компилятор выделит *30* целочисленных переменных.

## §5.2 Объявление и инициализация статических массивов

Объявление статических массивов в C++ и C# отличается.

**1. Объявление статических массивов в С++**

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

text

```text
тип_элемента имя_массива [количество_элементов] = {необязательные исходные значения};
```

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

cpp

```cpp
int MyNumbers [5] = {34, 56, -21, 5002, 365};
```

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

cpp

```cpp
int MyNumbers [5] = {100};
```

Здесь все элементы массива *MyNumbers* инициализируются значением *100*. Вы можете также инициализировать только часть элементов **массива**:

cpp

```cpp
int MyNumbers [5] = {100, 200};
```

Здесь проинициализированы только первые два элементы **массива** *MyNumbers*. При частичной инициализации массивов некоторые компиляторы инициализируют проигнорированные вами элементы исходным значением **0**.

Если исходное количество элементов в массиве неизвестно, можно не указывать его:

cpp

```cpp
int MyNumbers [] = {100, 200, 300};
```

Приведенный выше код создает **массив длиной равной трем**.

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

**2. Объявление статических массивов в С#**

Объявление массива в С# похоже на объявление переменной за тем исключением, что после указания типа ставятся квадратные скобки:

text

```text
тип_элемента[] имя_массива;
```

Например, определим **массив** целых чисел:

csharp

```csharp
int[] numbers;
```

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

csharp

```csharp
int[] numbers = new int[5];
```

Здесь вначале мы объявили **массив** *nums*, который будет хранить данные типа *int*. Далее используя операцию **new**, мы выделили память для пяти элементов массива: `new int[5]`. Число **5** – здесь является длиной массива. При таком определении все элементы получают значение по умолчанию, которое предусмотрено для их типа. Для типа *int* значение по умолчанию – *0*.

Также мы сразу можем указать значения для этих элементов:

csharp

```csharp
int[] nums2 = new int[4] { 1, 2, 3, 5 };
int[] nums3 = new int[] { 1, 2, 3, 5 };
int[] nums4 = new[] { 1, 2, 3, 5 };
int[] nums5 = { 1, 2, 3, 5 };
```

Все перечисленные выше способы будут равноценны.

## §5.3 Доступ к данным, хранящимся в массиве

Чтобы использовать данные в **массиве**, необходимо обратиться к конкретному элементу. Это делается с помощью **индекса** — номера позиции элемента в массиве.

**Доступ к элементам массива**

— одна из основных операций при работе с массивами. Она позволяет читать значения, изменять их и обрабатывать по одному.

Каждый элемент **массива** имеет **уникальный номер позиции**, называемый **индексом**. Важно понимать, что **индекс массива** начинается с **0**, то есть первый элемент — под индексом **0**, второй — **1**, ..., последний — **n-1**, где **n** — *длина массива*.

wireframe

```
┌──────────────────────────────────────────────────┐

│                      МАССИВ                      │

├───────────────────┬───────┬───────┬──────────────┤

│ Элементы (Values) │  10   │  20   │  30   │  40  │

├───────────────────┼───────┼───────┼───────┼──────┤

│ Индексы (Indexes) │   0   │   1   │   2   │   3  │

└───────────────────┴───────┴───────┴───────┴──────┘
```

Для доступа к элементу используется квадратная скобка **[]** после имени массива:

text

```text
имя_массива[индекс]
```

Это выражение можно использовать:

- **Справа** — чтобы прочитать значение.
- **Слева** — чтобы записать (изменить) значение.

Чтобы получить значение элемента, используйте индекс в скобках:

```csharp
int[] numbers = {10, 20, 30};
int x = numbers[1];  // x = 20
Console.WriteLine(numbers[0]); // вывод: 10
```

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

Пример:

text

```text
numbers[1] = 99; // теперь массив: {10, 99, 30}
```

**Массивы изменяемы**

— их элементы можно менять после создания, но у статчиеских массивов **нельзя менять размер**!

Выражение `массив[индекс]` ведёт себя точно так же, как обычная переменная. То есть, его можно использовать в:

- В выражениях:

text

```text
int sum = numbers[0] + numbers[1];
```

- В условиях:

text

```text
if (numbers[2] > 0) 
{
	// Какое-то необходимое действие 
} 
```

- В вызовах функций:

text

```text
PrintValue(numbers[1]); 
```

- В циклах:

csharp

```csharp
for (int i = 0; i < 5; i++)
{
	Console.WriteLine(numbers[i]);
}
```

Информация

Очень важно! Индекс должен быть в допустимом диапазоне от **0** до **«длина – 1»**. В C#, в случае, выхода за границы, Вы получите исключение **«IndexOutOfRangeException»**. В С++ же нет проверки. Обращение к `arr[100]` при массиве из **5** элементов — ошибка времени выполнения , может повредить память, вызвать сбой.

Помимо того, что индекс может быть константой, как говорилось выше, индекс так же может быть переменной: `arr[i]` и выражением: `arr[2 * j - 1]`.

Пример:

```csharp
int i = 1;
int j = i + 1;
Console.WriteLine(arr[j]); // arr[2]
```

## §5.4 Многомерные массивы

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

Но что, если нам теперь нужно использовать массив для моделирования солнечных панелей?

![Рисунок 1 – Моделирование солнечных панелей](/images/lectures/oaip/05/image-01.webp)

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

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

### §5.4.1 Двумерные массивы

**Двумерный массив**

— это массив, элементы которого упорядочены по двум измерениям: строкам и столбцам. Его удобно представлять в виде таблицы или матрицы, где каждый элемент имеет два индекса: первый — номер строки (по вертикали) и второй — номер столбца (по горизонтали).

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

wireframe

```
┌──────────────────────────────────────────────────────────────────┐

│                    ИНДЕКСАЦИЯ МАТРИЦЫ [i, j]                     │

├───────────────┬───────────┬───────────┬───────────┬──────────────┤

│               │    [0]    │    [1]    │    [2]    │    [3]       │

├───────────────┼───────────┼───────────┼───────────┼──────────────┤

│      [0]      │    10     │    20     │    30     │    40        │

│      [1]      │    50     │    60     │    70     │    80        │

│      [2]      │    90     │   100     │   110     │   120        │

└───────────────┴───────────┴───────────┴───────────┴──────────────┘
```

Обращение к элементу:

- **matrix[0][1]** → 20 (первая строка, второй столбец)
- **matrix[2][3]** → 120 (третья строка, четвёртый столбец)

В C# двумерные массивы объявляются с использованием запятой в скобках: тип[,] имя:

text

```text
int[,] matrix; // объявление
```

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

csharp

```csharp
matrix = new int[3, 4]; 
// 3 строки, 4 столбца, все элементы = 0
```

Так же его сразу же можно проинициализировать:

csharp

```csharp
int[,] matrix = {
    {10, 20, 30, 40},
    {50, 60, 70, 80},
    {90, 100, 110, 120}
};
```

В отличие от C#, где **двумерный массив** — это единый объект-таблица, в C++ нет встроенного понятия *«матрица»*.

Вместо этого, двумерный массив реализуется как одномерный массив, элементами которого являются другие массивы.

cpp

```cpp
int matrix[3][4] = {
    {10, 20, 30, 40},
    {50, 60, 70, 80},
    {90, 100, 110, 120}
};
```

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

Объявление без инициализации происходит аналогично языку *С#*: *«int matrix[3][4];»*.

Так же, что в *С++*, что в *C#*, элементы массива можно заполнять поэтапно:

```csharp
matrix[0, 0] = 10;
matrix[0, 1] = 20;
// и т.д.
```

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

```csharp
массив[строка, столбец]
```

Предположим, мы хотим вывести элемент с индексом «1, 2», на двух языках:

```csharp
Console.WriteLine(matrix[1, 2]); 
```

Как и у **одномерных массивов**, у **двумерных массивов** есть **размер**, только в отличии от **одномерного**, **размерностью** счиатется **«количество строк х количество столбцов»**. Для того, чтобы в C# получить размер массива, используются два метода:

csharp

```csharp
int rows = matrix.GetLength(0); // строки
int cols = matrix.GetLength(1); // столбцы
```

В С++ встроенных функций для получения размера массивов нет. В этом случае, размеры необходимо хранить отдельно или вычислить с помощью **sizeof**:

cpp

```cpp
// Первый способ
int rows = 3;
int cols = 4;

// Второй способ
int rows = sizeof(matrix) / sizeof(matrix[0]);// строки
int cols = sizeof(matrix[0]) / sizeof(matrix[0][0]);// столбцы
// Только стоит учитывать, что sizeof
// только для статических массивов, объявленных в той же области
```

### §5.4.2 Многомерные массивы

Массивы могут иметь и большее количество измерений. Объявление трехмерного массива могло бы выглядеть так:

text

```text
int[,,] nums3 = new int[2, 3, 4];
```

Или на примере языка С++ объявим четырехмерный массив:

cpp

```cpp
int arr[2][3][4][5];// четырёхмерный массив: 2 × 3 × 4 × 5
```

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

Трёхмерные, четырёхмерные и вообще N-мерные массивы в C++ и C# (а также в других языках программирования) применяются в различных областях, где данные естественным образом представляются в виде многомерных структур, например, при разработке игр, где требуется трехмерная графика, и так далее.

Например, трехмерный массив при его выводе будет в следующем формате:

wireframe

```
┌──────────────────────────────────────────────────────────────────┐

│                      ТРЁХМЕРНЫЙ МАССИВ [2, 3, 4]                 │

├──────────────────────────────────────────────────────────────────┤

│                        СЛОЙ 0 (matrix[0, i, j])                  │

├──────────────────────────────────────────────────────────────────┤                                                               

│               │ Столбец 0 │ Столбец 1 │ Столбец 2 │ Столбец 3    │

├───────────────┼───────────┼───────────┼───────────┼──────────────┤

│ Строка 0      │     3     │    54     │     8     │              │

│ Строка 1      │     5     │    88     │     9     │              │

│ Строка 2      │     2     │    31     │     4     │              │

├──────────────────────────────────────────────────────────────────┤

│                        СЛОЙ 1 (matrix[1, i, j])                  │

├──────────────────────────────────────────────────────────────────┤ 

│               │ Столбец 0 │ Столбец 1 │ Столбец 2 │ Столбец 3    │

├───────────────┼───────────┼───────────┼───────────┼──────────────┤

│ Строка 0      │     2     │    25     │     7     │              │

│ Строка 1      │     5     │    29     │     9     │              │

│ Строка 2      │     9     │    41     │     5     │              │

└──────────────────────────────────────────────────────────────────┘
```

### §5.4.3 «Зубчатые массивы»

От многомерных массивов надо отличать массив массивов или так называемый «зубчатый массив»:

csharp

```csharp
int[][] nums = new int[3][];
nums[0] = new int[2] { 1, 2 };
// выделяем память для первого подмассива
nums[1] = new int[3] { 1, 2, 3 };
// выделяем память для второго подмассива
nums[2] = new int[5] { 1, 2, 3, 4, 5 };
// выделяем память для третьего подмассива
```

Важное уточнение

*С++* зубчатые массивы обозначаются с помощью **указателей на указатель** о чем будет рассмотрено во 2-м семестре курса **«Основы Алгоритмизации и Программирования»**.

Здесь две группы квадратных скобок указывают, что это массив массивов, то есть такой массив, который в свою очередь содержит в себе другие массивы. Причем длина массива указывается только в первых квадратных скобках, все последующие квадратные скобки должны быть пусты: new int[3][]. В данном случае у нас массив nums содержит три массива. Причем размерность каждого из этих массивов может не совпадать.

wireframe

```
┌──────────────────────────────────────────────────────────────────┐

│                   ЗУБЧАТЫЙ МАССИВ (C#)                           │

├───────────────┬──────────────────────────────────────────────────┤

│ Индекс (nums) │ Ссылка на подмассив (значения)                   │

├───────────────┼──────────────────────────────────────────────────┤

│    nums[0]    │ ┌───┬───┐                                        │

│               │ │ 1 │ 2 │ (размер: 2)                            │

│               │ └───┴───┘                                        │

│    nums[1]    │ ┌───┬───┬───┐                                    │

│               │ │ 1 │ 2 │ 3 │ (размер: 3)                        │

│               │ └───┴───┴───┘                                    │

│    nums[2]    │ ┌───┬───┬───┬───┬───┐                            │

│               │ │ 1 │ 2 │ 3 │ 4 │ 5 │ (размер: 5)                │

│               │ └───┴───┴───┴───┴───┘                            │

└───────────────┴──────────────────────────────────────────────────┘
```

Примеры всех массивов можно рассмотреть ниже:

![Рисунок 1 – Виды массивов в языках программирования](/images/lectures/oaip/05/image-02.webp)

Причем можно использовать в качестве массивов и многомерные:

csharp

```csharp
int[][,] nums = new int[3][,]
{
    new int[,] { {1,2}, {3,4} },
    new int[,] { {1,2}, {3,6} },
    new int[,] { {1,2}, {3,5}, {8, 13} }
}; 
```

## §5.5. Массивы в параметрах методов (C#)

Массивы часто используются как входные данные для методов. В C# **передача массива в метод** — простая и удобная операция, поскольку массивы являются ссылочными типами.

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

Чтобы принять массив в качестве параметра, укажите тип массива в списке параметров:

csharp

```csharp
void PrintArray(int[] arr)
{
    for (int i = 0; i < arr.Length; i++)
    {
        Console.Write(arr[i] + " ");
    }

    Console.WriteLine();
}
```

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

csharp

```csharp
int[] numbers = {1, 2, 3, 4, 5};
PrintArray(numbers); // вывод: 1 2 3 4 5
```

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

csharp

```csharp
void DoubleValues(int[] arr)
{
    for (int i = 0; i < arr.Length; i++)
    {
        arr[i] *= 2;
    }
}

int[] data = {1, 2, 3};
DoubleValues(data);
Console.WriteLine(data[0]); // 2 — исходный массив изменён!
```

## §5.6 Массивы и циклы

Известно, что индекс массива не обязательно должен быть константным значением — он может быть и обычной переменной. Это означает, что мы можем использовать счетчик цикла в качестве индекса массива для доступа к элементам и выполнения с ними необходимых математических и других операций. Когда цикл используется для доступа к каждому элементу массива поочередно, то это называются **итерацией по массиву**, то есть проход по каждому элементу массива.

Например:

csharp

```csharp
for (int i = 0; i < arr.Length; i++)
{
    Console.WriteLine(arr[i]);
}
```

Цикл **for** идеально подходит для работы с массивами, потому что:

- Позволяет управлять индексом (i).
- Условие i < arr.Length гарантирует безопасный доступ.
- Подходит для любого типа массива.

Вывод элементов массива на языке С# и C++ работает аналогично:

```csharp
int[] numbers = {10, 20, 30, 40, 50};
for (int i = 0; i < numbers.Length; i++)
{
    Console.WriteLine($"Элемент [{i}] = {numbers[i]}");
}
```

В *C#* и в *С++* есть специальный цикл **foreach**, предназначенный для перебора всех элементов коллекции, включая массивы.

Он не работает с индексами, но защищает от выхода за границы.

csharp

```csharp
int[] numbers = {1, 2, 3, 4, 5};
foreach (int value in numbers)
{
    Console.Write(value + " ");
}
```

В **лекции №3** мы узнали, что в С++ есть аналог цикла **foreach** из С#, его сигнатура выглядит следующим образом:

text

```text
for(type variable_name : array)
```

Для примера перепишем код выше на *С++* с использованием аналога цикла **foreach**:

cpp

```cpp
int numbers[] = {1, 2, 3, 4, 5};
for (int i : numbers)
{
    std::cout << "Элемент [" << i << "];
}
```

Для двумерных и более массивов используются вложенные циклы. Для начала напомним, что язык С# предоставляет встроенные методы для вычисления размера, например, **метод** – это *GetLenght(dim)*, где **dim** принимает 3 значения, 0 – количество строк, 1 – количество столбцов, 2 – третье измерение (для трехмерных и более массивов).

В С++ нет встроенных методов, поэтому приходится выполнять небольшую логику:

cpp

```cpp
int matrix[3][4];
int rows = sizeof(matrix) / sizeof(matrix[0]); // 3
int cols = sizeof(matrix[0]) / sizeof(matrix[0][0]);  // 4
```

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

```csharp
int[,] matrix = { // С#
    {1, 2},
    {3, 4}
};

for (int i = 0; i < matrix.GetLength(0); i++)
{
    for (int j = 0; j < matrix.GetLength(1); j++)
    {
       Console.Write(matrix[i, j] + " ");
    }
        
    Console.WriteLine();
}
```
