Добавил:
Преподаватель Колледжа информационных технологий Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Лекции / МАССИВЫ

.pdf
Скачиваний:
40
Добавлен:
08.05.2022
Размер:
867.93 Кб
Скачать

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

равноценны:

 

void print(int numbers[]);

 

void print(int *numbers);

 

Передадим в функцию массив:

 

Листинг 8.2.

 

 

 

1

#include <iostream>

 

2

using namespace std;

 

3

void print(int[]);

 

4

int main()

 

5

{

 

6

int nums[] = {1, 2, 3, 4, 5};

 

7

print(nums);

 

8

return 0;

 

9

}

 

10

void print(int numbers[])

 

11

{

 

12

cout << "First number: " << numbers[0] << endl;

 

13

}

 

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

§8.6 Массивы и циклы

§8.6.1 Обработка массивов на С++

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

Например:

Листинг 8.3.

1#include <iostream>

2using namespace std;

11

3

int main()

4

{

5

int numbers[4] = {1,2,3,4};

6

int size = sizeof(numbers)/sizeof(numbers[0]);

7

for(int i=0; i < size; i++)

8

cout << numbers[i] << endl;

9

return 0;

10

}

Чтобы пройтись по массиву в цикле, вначале надо найти длину массива.

Для нахождения длины применяется оператор sizeof. По сути, длина массива равна совокупной длине его элементов. Все элементы представляют один и тот же тип и занимают один и тот же размер в памяти. Поэтому с помощью выражения sizeof(numbers) находим длину всего массива в байтах, а с помощью выражения sizeof(numbers[0]) - длину одного элемента в байтах. Разделив два значения, можно получить количество элементов в массиве. А далее с помощью цикла for перебираем все элементы, пока счетчик i не станет равным длине массива.

А вот пример использования цикла для поиска в массиве наибольшего значения (наилучшей оценки среди всех студентов в группе):

Листинг 8.4.

1

#include <iostream>

2

using namespace std;

3

int main()

4

{

5

int students[] = { 73, 85, 84, 44, 78};

6

const int numStudents = sizeof(students) /

 

sizeof(students[0]);

7

int maxScore = 0; // отслеживаем самую высокую

 

оценку

8

for (int person = 0; person < numStudents;

 

++person)

9

if (students[person] > maxScore)

10

maxScore = students[person];

11

cout << "The best score was " << maxScore << '\n';

12

return 0;

13

}

 

12

Здесь уже используется переменная maxScore (не из цикла) для отслеживания самого большого значения массива. Сначала инициализируем maxScore значением 0, что означает, что мы еще не видели никаких оценок.

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

Циклы с массивами обычно используются для выполнения одной из 3-х

следующих задач:

Вычислить значение (например, среднее или сумму всех

значений);

Найти значение (например, самое большое или самое маленькое);

Отсортировать элементы массива (например, по возрастанию или по убыванию).

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

При поиске значения, переменная обычно используется для хранения наилучшего варианта (или индекса наилучшего варианта) из всех просмотренных. В примере, приведенном выше, где мы используем цикл для поиска наивысшей оценки, переменная maxScore используется для хранения наибольшего количества баллов из просмотренных ранее элементов массива.

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

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

Ошибку на единицу (или «ошибку неучтенной единицы») сделать легко, а

13

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

Листинг 8.5.

1

#include <iostream>

2

using namespace std;

3

int main()

4

{

5

int students[] = { 73, 85, 84, 44, 78 };

6

const int numStudents = sizeof(students) /

 

sizeof(students[0]);

7

int maxScore = 0; // отслеживаем самую высокую

 

оценку

8

for (int person = 0; person <= numStudents;

 

++person)

9

if (students[person] > maxScore)

10

maxScore = students[person];

11

cout << "The best score was " << maxScore;

12

return 0;

13

}

Здесь проблема состоит в неверном условии оператора if в цикле for!

Объявленный массив содержит 5 элементов, проиндексированных от 0 до 4.

Однако цикл внутри перебирает элементы от 0 до 5. Следовательно, на последней итерации в цикле for выполнится:

if (students[5] > maxScore)

maxScore = students[5];

Но ведь students[5] не определен! Его значением, скорее всего, будет простой мусор. И в итоге результатом выполнения цикла может быть ошибочный maxScore.

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

§8.6.2 Обработка массивов на С#

14

Если мы хотим отдельно пробежаться по каждой строке в таблице, то надо получить количество элементов в размерности. В частности, у каждого массива есть метод GetUpperBound(dimension), который возвращает индекс последнего элемента в определенной размерности. И если мы говорим непосредственно о двухмерном массиве, то первая размерность (с индексом

0), по сути, это и есть таблица. И с помощью выражения mas.GetUpperBound(0) + 1 можно получить количество строк таблицы,

представленной двухмерным массивом. А через mas.Length / rows можно получить количество элементов в каждой строке:

 

Листинг 8.6.

 

 

 

1

int[,] mas = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 },

 

 

{ 10, 11, 12 } };

 

2

int rows = mas.GetUpperBound(0) + 1;

 

3

int columns = mas.Length / rows;

 

4

// или так

 

5

// int columns = mas.GetUpperBound(1) + 1;

 

6

for (int i = 0; i < rows; i++)

 

7

{

 

8

for (int j = 0; j < columns; j++)

 

9

{

 

10

Console.Write($"{mas[i, j]} \t");

 

11

}

 

12

Console.WriteLine();

 

13

}

 

 

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

Например:

 

Листинг 8.8.

 

 

1

int[][] numbers = new int[3][];

 

2

numbers[0] = new int[] { 1, 2 };

 

3

numbers[1] = new int[] { 1, 2, 3 };

 

4

numbers[2] = new int[] { 1, 2, 3, 4, 5 };

 

5

// перебор с помощью цикла for

 

6

for (int i = 0; i<numbers.Length;i++)

 

7

{

 

8

for (int j =0; j<numbers[i].Length; j++)

 

9

{

 

 

15

 

10 Console.Write($"{numbers[i][j]} \t");

11}

12Console.WriteLine();

13}

Для перебора элементов массивов и контейнейров в C# предусмотрен

специальный вид циклов – foreach. Формальное объявление цикла foreach

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

1

foreach(type identifier in container)

 

 

2

{

 

 

3

// операторы

 

 

4

}

 

 

 

где

type – тип переменной с именем identifier;

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

identifier должен совпадать с типом массива или контейнера. Связь между

identifier и container реализуется с помощью союза in;

container – имя коллекции или массива, который обрабатывается.

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

(коллекции) container. На каждом следующем шаге итерации выбирается следующий элемент из container, который сохраняется в переменной identifier. Цикл завершается, когда будут пересмотрены все элементы массива (коллекции) container.

Пример реализации данного цикла:

1

int[] numbers = new int[] { 1, 2, 3, 4, 5 };

 

 

2

foreach (int i in numbers)

 

 

3

{

 

 

4

Console.WriteLine(i);

 

 

 

16

5 }

Здесь в качестве контейнера выступает массив данных типа int. Поэтому

мы объявляем переменную с типом int

Подобные действия мы можем сделать и с помощью цикл for:

1

int[] numbers =

new int[] { 1, 2, 3, 4, 5 };

 

 

 

2

for (int i = 0;

i < numbers.Length; i++)

 

 

 

3

{

 

 

 

4

Console.WriteLine(numbers[i]);

 

 

 

5

}

 

 

 

 

В то же время цикл for более гибкий по сравнению с foreach. Если foreach

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

17