Массив — набор элементов одного и того же типа, объединенных общим именем. Массивы в С# можно использовать по аналогии с тем, как они используются в других языках программирования, например, в C++ или Pascal. Однако С#-массивы имеют существенные отличия. Во-первых, они относятся к ссылочным типам данных. При этом имя массива является ссылкой на область кучи (динамической памяти), в которой последовательно размещается набор элементов определенного типа. Выделение памяти под элементы происходит на этапе объявления или инициализации массива, а за освобождением памяти следит сборщик мусора. Во-вторых, массивы реализуются в С# как объекты, для которых разработан большой набор методов, реализующих различные алгоритмы обработки элементов массива.
Рассмотрим следующие типы массивов: одномерные, многомерные и ступенчатые (разрывные).
Одномерные массивы
Одномерный массив – это фиксированное количество элементов одного и того же типа, объединенных общим именем, где каждый элемент имеет свой номер. Нумерация элементов массива в С# начинается с нуля, то есть, если массив состоит из 10 элементов, то они будут иметь следующие номера: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9.
Одномерный массив в С# реализуется как объект, поэтому его создание представляет собой двухступенчатый процесс. Сначала объявляется ссылочная переменная типа массив, затем выделяется память под требуемое количество элементов базового типа, и ссылочной переменной присваивается адрес нулевого элемента в массиве. Базовый тип определяет тип данных каждого элемента массива. Количество элементов, которые будут храниться в массиве, определяется размером массива.
При необходимости, этапы объявления переменной типа массив, и выделения необходимого объема памяти могут быть объединены в один. Кроме того, на этапе объявления массива можно произвести его инициализацию. Поэтому, для объявления одномерного массива может использоваться одна из следующих форм записи:
1) базовый_тип [] имя_массива;
Например:
Объявлена ссылка на одномерный массив символов (имя ссылки а), которая в дальнейшем может быть использована для: адресации на уже существующий массив; передачи массива в метод в качестве параметра; отсроченного выделения памяти под элементы массива.
2) базовый_тип [] имя_массива = new базовый_тип [размер];
Например:
Объявлена ссылка b на одномерный массив целых чисел. Выделена память для 10 элементов целого типа, адрес этой области памяти записан в ссылочную переменную b. Элементы массива инициализируются по умолчанию нулями.
Замечание. Надо отметить, что в C# элементам массива присваиваются начальные значения по умолчанию в зависимости от базового типа. Для арифметических типов – нули, для ссылочных типов – null, для символов - символ с кодом ноль.
3) базовый_тип [] имя_массива={список инициализации};
Например:
double[] c = { 0.3, 1.2, -1.2, 31.5 };
|
Объявлена ссылка c на одномерный массив вещественных чисел. Выделена память под одномерный массив, размерность которого соответствует количеству элементов в списке инициализации - четырем. Адрес этой области памяти записан в ссылочную переменную с. Значение элементов массива соответствует списку инициализации.
Обращение к элементу массива происходит с помощью индекса: указывается имя массива и, в квадратных скобках, номер данного элемента. Например, a[0], b[8], c[i].
Так как массив представляет собой набор элементов, объединенных общим именем, то обработка массива обычно производится в цикле. Рассмотрим несколько основных примеров работы с одномерными массивами.
Вывод массива на экран
static void Main()
{
int[] myArray = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
for (int i = 0; i < 10; i++)
{
Console.WriteLine(myArray[i]);
}
}
|
Для вывода одномерного массива на экран очень удобно использовать оператор foreach. Оператор foreach применяется для перебора элементов в специальном образом организованной группе данных, в том числе, и в массиве. Удобство этого вида цикла заключается в том, что нам не требуется определять количество элементов в группе и выполнять перебор по индексу – мы просто указываем элементы какой группы необходимо перебрать. Синтаксис оператора:
foreach (<тип><имя>in<группа>) <тело цикла>;
где имя определяет локальную по отношению к циклу переменную, которая будет по очереди перебирать все значения из указанной группы; ее тип соответствует базовому типу элементов группы.
Ограничением оператора foreach является то, что с его помощью можно только просматривать значения элементов в группе данных. Никаких изменений ни с самой группой, ни с находящимися в ней данными проводить нельзя.
В нашем случае осуществить вывод массива на экран можно следующим образом:
static void Main()
{
int[] myArray = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
foreach (int elem in myArray)
{
Console.WriteLine(elem);
}
}
|
Ввод элементов массива
static void Main()
{
int[] myArray; //создаем ссылку на массив
Console.Write("n= ");
int n = int.Parse(Console.ReadLine());
myArray = new int[n]; //выделяем память под массив требуемой длины
for (int i = 0; i < n; i++) //вводим элементы массива с клавиатуры
{
Console.Write("A[{0}]= ", i);
myArray[i] = int.Parse(Console.ReadLine());
}
foreach (int elem in myArray) //выводим массив на экран
{
Console.Write("{0} ", elem);
}
}
|
Заполнение массива случайными элементами
Заполнить массив данными можно с помощью генератора случайных чисел Для этого используется класс Random:
static void Main()
{
Random rnd = new Random(); //инициализируем генератор случайных чисел
int[] myArray;
int n = rnd.Next(5, 10); //генерируем случайное число из диапазона [5..10)
myArray = new int[n];
for (int i = 0; i < n; i++)
{
myArray[i] = rnd.Next(10); // заполняем массив случайными числами
}
foreach (int elem in myArray) //выводим массив на экран
{
Console.Write("{0} ", elem);
}
}
|
Контроль границ массива
Выход за границы массива в C# расценивается как критическая ошибка, которая ведет к завершению работы программы и генерированию стандартного исключения - IndexOutOfRangeException. Рассмотрим следующий фрагмент программы:
int[] myArray = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
Console.WriteLine(myArray[10]);
|
В данном случае описан массив из 10 элементов. Так как нумерация элементов массива ведется с нуля, то последний элемент имеет номер 9, и, следовательно, элемента с номером 10 не существует. В этом случае выполнение программы завершится, о чем на консоль будет выдано соответствующее сообщение.
Массив как параметр
Так как имя массива фактически является ссылкой, то он передается в метод по ссылке и, следовательно, все изменения элементов массива, являющегося формальным
параметром, отразятся на элементах соответствующего массива, являющегося фактическим параметром. При этом указывать спецификатор ref не нужно. Рассмотрим пример передачи массива в качестве параметра метода:
class Program
{
//выводит на экран массив a
static void Print(int[] a)
{
foreach (int elem in a)
{
Console.Write("{0} ", elem);
}
Console.WriteLine();
}
//Заменяет отрицательные элементы массива а на нули. Размерность массива n.
static void Change(int[] a, int n)
{
for (int i = 0; i < n; i++)
{
if (a[i] < 0)
{
a[i] = 0;
}
}
}
static void Main()
{
int[] myArray = { 0, -1, -2, 3, 4, 5, -6, -7, 8, -9 };
Console.Write("Исходный массив: ");
Print(myArray);
Change(myArray, 10);
Console.Write("Измененный массив: ");
Print(myArray);
}
}
|
То, что имя массива является ссылкой, следует учитывать при попытке присвоить один массив другому. Рассмотрим следующий пример:
class Program
{
static void Print(int[] a)
{
foreach (int elem in a)
{
Console.Write("{0} ", elem);
}
Console.WriteLine();
}
static void Main()
{
int[] one = { 1, 2, 3, 4, 5 };
Console.Write("Первый массив: ");
Print(one);
int[] two = { 6, 7, 8, 9 };
Console.Write("Второй массив: ");
Print(two);
one = two;
two[0] = -100;
Console.WriteLine("После присвоения ");
Console.Write("Первый массив: ");
Print(one);
Console.Write("Второй массив: ");
Print(two);
}
}
|
Результат работы программы:
Первый массив: 1 2 3 4 5
Второй массив: 6 7 8 9
После присвоения
Первый массив: -100 7 8 9
Второй массив: -100 7 8 9
Таким образом, ссылки one и two ссылаются на один массив. А исходный массив, связанный сo ссылкой one, оказался потерянным, и будет удален сборщиком мусора.
|