Скачиваний:
69
Добавлен:
24.03.2015
Размер:
262.66 Кб
Скачать

Глава 7. Массивы

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

Напомним, что система типов языка С# построена на основе классов. Типы делятся на четыре группы (значений, ссылок, указателей и тип void). С типами значений мы уже знакомы. К типам ссылок отнесены собственно классы (библиотечные и определяемые программистом), массивы и строки. Рассмотрим массивы.

Одномерный массив – набор однотипных элементов, доступ к которым осуществляется с помощью выражения с операцией индексирования:

имя_ссылки_на_массив [индексирующее_выражение]

Здесь имя_ссылки_на_массив - ссылка типа, производного от класса Array. Индексирующее_выражение должно иметь целочисленный тип (или приводится к целочисленному типу). Значение индексирующего выражения (иначе индекс) должно принадлежать фиксированному конечному диапазону [О, indMax], где 0 - нижняя граница, indMax - верхняя граница индекса. indMax однозначно определяет количество элементов (размер) массива, равное indMax+1.

Имя_ссылки_на_массив - выбираемый пользователем идентификатор. Чтобы идентификатор стал именем ссылки на массив, используется объявление вида:

тип [] имя_ссылки_на_массив;

Этим оператором объявляется переменная типа ссылки на массив, а тип определяет тип элементов этого массива. Кроме того, объявление указанного формата вводит в программу новый тип с обозначением тип[].

Примеры:

int [ ] integers;

double [ ] points;

Здесь integers - ссылка на массив типа int [ ] с элементами типа int; points -ссылка на массив типа double [ ] с элементами типа double.

В результате обработки таких объявлений компилятор выделит в стеке участки для размещения переменных (ссылок) integers и points. Однако, массивов, которые могут адресовать эти ссылки, пока не существует, и значениями integers и points является null. Каждый конкретный массив создаётся как объект класса, производного от класса Array или соответствующего ему системного класса System.Array.

Для создания экземпляра (объекта) конкретного типа массивов используется операция new. Выражение:

new тип [размер-массива]

определяет объект-массив с заданным в квадратных скобках количеством элементов указанного типа. Этот объект компилятор размещает в области памяти, называемой кучей (heap). Результат выполнения операции new -ссылка на выделенный для объекта участок памяти.

Чтобы связать ссылку с этим объектом используют операцию присваивания:

имя_ссылки_на_массив= new тип[размер-массива];

Тип ссылки на массив должен соответствовать типу, указанному после операции new.

Примеры:

integers=new int [14];

points=new double [8];

После этих и предыдущих объявлений ссылка integers (размещённая в стеке) будет адресовать (ссылаться на) участок памяти в куче, отведённый для конкретного массива из 14-ти элементов типа int. Ссылка points будет связана с массивом из 8-ми элементов типа double.

Допустимо объединение объявления ссылки на массив и определения массива-объекта:

тип [ ] имя_ссылки_на_массив=new тип [размер_массива];

Пример:

char[] line = new char[12];

Такое объявление вводит тип char[], определяет объект-массив char[12], объявляет ссылку line на массив с элементами типа char и связывает эту ссылку с объектом-массивом.

После определения массива его элементы получают умалчиваемые значения. Для элементов арифметических типов по умолчанию устанавливаются нулевые значения, для элементов типа char - значения '\0', для элементов логического типа - значения false, для элементов типа string

  • пустые строки, для элементов с типами ссылок и для элементов типа object

  • значения null.

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

// 07_01.cs - массивы - инициализация по умолчанию

static void Main01()

{

bool[] bar = new bool[5];

Console.WriteLine("bar[2] = " + bar[2]);

int[] integers = new int[14];

Console.WriteLine("integers[0] = " + integers[0]);

double[] points = new double[8];

Console.WriteLine("points[7] = " + points[7]);

}

Результат выполнения программы: bar[2] = False integers[0] = 0 points[7] = 0

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

При создании экземпляра (объекта) типа массивов разрешена его явная инициализация, т.е. общий вид выражения с операцией new таков:

new тип [размер-массива] инициализатор;

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

{ список_выражений }

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

Пример:

double a = 1.1;

double [4] x=new double [4] {2.0/4, 3.0, 4.0, а};

Здесь х – ссылка на массив с элементами типа double, элементы имеют значения:

х[0] ==0.5 х[1] ==3.0 х[2] ==4.0 х[3] == 1.1

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

Для объявления экземпляра массива с применением инициализатора допустима и зачастую используется сокращённая форма без явного использования операции new:

тип [ ] имя_ссылки_на_массив = инициализатор

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

long[] g = {8/4, 3L, 4, (int)2.4};

Тот же результат можно получить, явно применив операцию new:

long [ ] s = new long []{8/4, 3L, 4, (int)2.4};

В одном объявлении могут быть определены несколько однотипных ссылок на массивы с одинаковыми типами элементов:

byte [] х, у, z;

Инициализация массивов допустима и при таком определении нескольких однотипных ссылок в одном объявлении. Например, так:

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

int[] z = new int[] { 6, 7, 8, 9 };

int[] d = z;

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

Определяя ссылки на массивы и массивы как объекты, необходимо помнить их различия. Компилятор в стеке выделяет память для каждой ссылки при её объявлении. А память в куче для объекта-массива выделяется только при явном (или неявном) применении операции new.

В данном примере ссылки d и z адресуют один и тот же массив (объект класса, производного от Array) из четырёх элементов типа int. Таким образом, к одному и тому же элементу массива в нашем примере возможен доступ с помощью двух ссылок. В результате выполнения операторов

d[3] = 43;

Console.WriteLine("z[3] = " + z[3]);

будет выведено z[3]=43.

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

К массивам (которые являются на самом деле частным случаем контейнеров или коллекций) применим ещё не рассмотренный нами оператор цикла, вводимый служебным словом foreach. Формат этого оператора:

foreach (тип имя_переменной in ссылка_на_массива)

тело_цикла

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

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

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

// 07_02.cs - массивы одномерные и цикл foreach

static void Main02()

{

char[] hi = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'С', '#', '!' };

foreach (char ch in hi)

Console.Write(ch);

Console. WriteLine();

}

Результат выполнения программы: Hello, C#!

Обратите внимание, что при объявлении объекта-массива и адресующей его ссылки hi выполнена инициализация массива, и не потребовалось явно применять операцию new.

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

Наиболее типичный случай - размер массива определяет пользователь, вводя нужное для решения задачи числовое значение.

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

do { цифра = число%10; число = число/10;

}

while(число!=0);

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

Если добавить в тело цикла оператор преобразующий цифры в символьное представление, то получим изображение числа в обратном порядке, т.е. для числа 753 получим цифры '3', '5', 7'. Чтобы получить последовательность цифр в нужном порядке можно использовать массив с элементами типа char. Размер массива заранее неизвестен и определяется значением (длиной) обрабатываемого числа. Длину числа (число цифр) можно вычислить, используя десятичный логарифм. В языке С# это можно сделать с помощью метода с заголовком double Math.Log10(double value). Метод возвращает значение double, а размер массива должен быть задан целым числом, поэтому придётся выполнить приведение типов.

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

// 07_03.cs - массив с заранее не определенным размером

static void Main03()

{

int number;

do

Console.Write("Введите целое положительное число: ");

while (!int.TryParse(Console.ReadLine(), out number) || number <= 0);

int len = (int)Math.Log10(number) + 1;

char[] ciphers = new char[len];

int figure, i = len - 1;

do

{

figure = number % 10;

number = number / 10;

ciphers[i--] = (char)(figure + '0');

}

while (number != 0);

Console.Write("Цифры числа:");

foreach (char ch in ciphers)

Console.Write(" " + ch); Console.WriteLine();

}

Результат выполнения программы:

Введите целое положительное число :rws<ENTER>

Введите целое положительное число: 975310<ENTER>

Цифры числа: 9 7 5 3 10

В программе int number - число, вводимое пользователем. Для чтения и последующего преобразования введённой последовательности цифр в числовое значение используются уже известные нам статические методы Console.ReadLine() и int.TryParse(). Переменная int len в объявлении получает значение количества цифр во введённом числе. Она определяет длину символьного массива, адресуемого ссылкой ciphers. Переменная int figure последовательно принимает значения цифр числа. Переменная int i служит индексом при записи в массив изображений цифр. Обратите внимание на выражение (char)(figure+'0'). В скобках выполняется сложение значения цифры (переменная figure) с кодом символа '0'. Тем самым формируется числовой код символа, соответствующего значению цифры. Так как элементы массива имеют тип char, то необходимо явное приведение типа (char). После записи изображения каждой цифры в массив индекс i уменьшается на 1.

Цикл foreach осуществляет прямой перебор массива ciphers[] и цифры на экран выводятся в правильном порядке. Результаты выполнения программы иллюстрируют сказанное.

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

Соседние файлы в папке Lekc_C#