Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

CSBasicCourse2ndedPodbelsky / CSBasicCourse2ndedPodbelsky

.pdf
Скачиваний:
34
Добавлен:
22.03.2016
Размер:
2.08 Mб
Скачать

массивов, то есть двумерные (матрицы), трехмерные параллелепипеды») и т.д.

Именно такие массивы традиционно принято называть многомерными массивами.

Примеры:

int [,] dots;

//ссылка на двумерный массив

byte [,,] bits;

//ссылка на трёхмерный массив

Для определения объекта конкретного экземпляра типа многомерных массивов используется выражение с операцией new

new тип_не_массива [d1,d2, d3 …] инициализатор

Здесь di – размер - количество элементов по соответствующему измерению.

Инициализатор представляет собой вложение конструкций

{список_инициализаторов}.

Элементами такого списка в свою очередь служат заключенные в фигурные скобки списки инициализаторов. Глубина вложения соответствует размерности массива. Размерность массива "массив:размерность массива" (его ранг) можно получить с помощью нестатического свойства Rank.

Пример определения с использованием инициализации матрицы (двумерного массива) с размерами 4 на 2:

int [,] matr = new int[4,2] {{1,2},{3,4},{5,6},{7,8}};

Как и для одномерных массивов при наличии инициализатора конструкцию new int[4,2] можно опустить.

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

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

клавиатуры:

// 07_05.cs - двумерный массив - единичная матрица using System;

class Program

{

static void Main()

{

int size;

do Console.Write("size = ");

while (!int.TryParse(Console.ReadLine(), out size) || size < 1); int[,] one = new int[size, size];

for (int i = 0; i < size; i++, Console.WriteLine()) for (int j = 0; j < size; j++)

{

if (i == j) one[i, j] = 1; Console.Write(one[i, j] + "\t");

 

 

}

 

 

foreach (int mem in one)

 

 

Console.Write(mem+" ");

 

Console.WriteLine();

 

Console.WriteLine("one.Length = " + one.Length);

 

Console.WriteLine("one.Rank = " + one.Rank);

}

}

 

 

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

 

size = 4<ENTER>

1

0

0

0

0

1

0

0

0

0

1

0

0

0

0

1

1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1

one.Length = 16

one.Rank = 2

 

 

В программе формируется двумерный массив (экземпляр массивов типа int[,]),

размеры которого определены переменной size. Значение size вводит пользователь.

По умолчанию все элементы массива получают нулевое значение. Во вложенных

циклах диагональным элементам присвоены единичные значения, и выводятся

 

значения всех элементов. Обратите внимание, как в заголовке внешнего цикла

for

используется обращение к методу Console.WriteLine( ) для перехода на новую

строку при выводе. Далее иллюстрируется применение цикла

foreach к

многомерному массиву. Перебор значений массива, представляющего матрицу,

выполняется по строкам. (Быстрее изменяется правый индекс.)

 

 

В конце программы выведено значение свойства one.Length

это

общее

количество элементов в массиве и значение ранга массива "массив:ранг массива" –

ранг равен двум.

7.4. Массивы массивов и непрямоугольные массивы

Как следует из синтаксического определения, при объявлении типа массива

можно задавать несколько спецификаторов размерностей. В стандарте приведён пример такого типа массивов (с тремя спецификаторами размерностей):

int [ ] [,,][,] – одномерный массив, элементы которого - трёхмерные массивы,

каждый с элементами типа "двумерный массив с элементами типа

int". Такой

массив можно рассматривать как массив массивов.

 

Так как размеры массивов, входящих как элементы в другой массив, могут

быть разными, то массив массивов

"массив:массив массивов" в общем случае не

является

"прямоугольным"

. Такие непрямоугольные массивы

"массив:непрямоугольные массивы"

в стандарте C# названы

jagged array

(зубчатые массивы). Пример из стандарта:

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

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

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

Обратите внимание, что с помощью одной операции new невозможно создать массив массивов. В примере j2 – ссылка на объект типа массив массивов с элементами типа int. Выражение new int[3][ ] создаёт объект-массив с тремя элементами типа "ссылка на одномерный массив с элементами типа int". Каждая из этих ссылок доступна с помощью соответствующего выражения j2[0], j2[1], j2[2].

Однако, вначале значения этих ссылок не определены. Только присвоив каждой из них результат выражения new int[ ] инициализатор, мы связываем ссылки с

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

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

инициализации (количество операций new при этом не изменится): int[ ][ ] j3 = new int[3][ ] {

new int[ ] { 1, 2, 3 },

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

new int[ ] { 1, 2, 3, 4, 5, 6, 7, 8, 9} };

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

Применяя инициализатор, разрешено опускать для массива массивов

выражение с операцией new, например, таким образом: int[ ][ ] j4 = {

new int[3], new int[6], new int[9] };

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

 

инициализаторов. Размеры массивов нижнего уровня с элементами типа

int здесь

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

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

следующую программу, формирующую нижнюю нулевую треугольную матрицу.

Элементам её диагонали присвоим номера строк (нумерацию будем выполнять,

начиная от 1). Как в примере с единичной матрицей размер матрицы (число её

строк) будет вводить пользователь, как значение переменной

int size. Текст

программы:

 

// 07_06.cs - нижняя треугольная матрица

 

using System;

 

class Program

 

{

 

static void Main()

 

{

 

int size;

 

do Console.Write("size = ");

 

while (!int.TryParse(Console.ReadLine(), out size) || size < 1);

 

int[ ][ ] tre = new int[size][];

 

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

 

{

tre[j] = new int[j + 1]; tre[j][j] = j + 1;

}

for (int i = 0; i < tre.Length; i++, Console.WriteLine()) for (int j = 0; j < tre[i].Length; j++)

Console.Write(tre[i][j] + "\t"); Console.WriteLine("tre.Length = " + tre.Length); Console.WriteLine("tre.Rank = " + tre.Rank);

}

}

Результат выполнения программы: size = 4<ENTER>

1

2

 

 

0

3

 

0

0

4

0

0

0

tre.Length = 4 tre.Rank = 1

В программе объявлена ссылка tre на массив массивов. Операцией new

определён массив из size элементов ссылок на массивы. Каждый элемент tre[j] –

ссылка на ещё не существующий одномерный массив с элементами типа int. Эти

массивы реальные строки треугольной матрицы формируются в цикле. Длина j–

го массива равна j+1.

В цикле печати массива для определения числа элементов используется свойство Length. Выражение tre.Length возвращает число строк матрицы. Обратите внимание, что в отличие от многомерного массива свойство Length равно числу элементов только "верхнего" уровня массива массивов. tri[j].Length позволяет определить длину j-й строки. Свойство Rank, относящееся к объекту типа int[ ][ ],

равно 1, т.к. это одномерный массив ссылок на массивы "массив:массив ссылок на массивы" . Остальное очевидно из результатов выполнения программы.

Вводя ссылку на массив и объявляя конкретный объект экземпляр массива,

программист каждый раз определяет некоторый тип именно таких массивов,

которые ему нужны. Синтаксис объявления этих типов мы уже разобрали и

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

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

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

(как наследник) системного класса Array. Будучи наследником, каждый тип массивов получает или по-своему реализует методы и свойства класса Array.

Следующая программа иллюстрирует возможности некоторых методов, о которых

мы ещё не говорили.

// 07_07.cs – методы и свойства класса Array using System;

class Program

{

static void Main()

{

double[,] ar = {

{10, -7, 0, 7},

{-3, 2.099, 6, 3.901},

{5, -1, 5, 6},

};

Console.WriteLine("ar.Rank = " + ar.Rank); Console.WriteLine("ar. = " + ar.GetUpperBound(1);

Console.WriteLine("ar.GetLength(1) = " + ar.GetLength(1)); for (int i = 0; i < ar.GetLength(0); i++, Console.WriteLine()) for (int j = 0; j <= ar.GetUpperBound(1); j++)

Console.Write("\t"+ ar[i, j]);

}

}

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

ar.Rank = 2

 

 

ar.GetUpperBound(1) = 3

 

ar.GetLength(1) = 4

7

 

10 -7

0

3,901

3

2,099

6

5

-1

5

6

В программе определён и инициализирован двумерный массив с элементами

типа double. Результаты выполнения программы поясняют особенности свойств и

методов типа массивов, производного от класса Array. Обратите внимание, что

GetUpperBound(1) – верхняя граница второго индекса, а не количество значений

этого индекса.

7.5. Массивы массивов и поверхностное копирование

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

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

Метод Clone() позволяет создать новый экземпляр массива. В программе

07_04.cs показано, что изменяя один из одномерных массивов-копий, мы не изменяем второй. Следующая программа иллюстрирует применение копирования к

многомерному массиву:

// 07_08.cs - двумерный массив - полное клонирование using System;

class Program

{

static void Main( )

{

int size;

do Console.Write("size = ");

while (!int.TryParse(Console.ReadLine(), out size)||size<1); int[,] one = new int[size, size]; Console.WriteLine("Массив one:");

for (int i = 0; i < size; i++, Console.WriteLine()) for (int j = 0; j < size; j++)

{

if (i == j) one[i, j] = 1;

Console.Write(one[i, j] + "\t");

}

Console.WriteLine("one.Length = " + one.Length); int[,] two = (int[,])one.Clone(); // клонирование two[0, 0] = -size;

Console.WriteLine("Массив two:");

for (int i = 0; i < size; i++, Console.WriteLine()) for (int j = 0; j < size; j++)

Console.Write(two[i, j] + "\t"); Console.WriteLine("Массив one:");

for (int i = 0; i < size; i++, Console.WriteLine()) for (int j = 0; j < size; j++)

Console.Write(one[i, j] + "\t");

}

}

 

 

 

 

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

size = 4<ENTER>

Массив one:

0

1

0

0

0

1

0

0

0

0

1

0

0

0

0

1

one.Length = 16

Массив two:

0

-4

0

0

0

1

0

0

0

0

1

0

0

0

0

1

Массив one:

0

1

0

0

0

1

0

0

0

0

1

0

0

0

0

1

В программе определена ссылка two типа

int[,], и ей присвоен результат

копирования массива, связанного со ссылкой one, имеющей тот же тип

int[,].

Выведена единичная матрица, адресованная ссылкой one, затем изменён обычным

присваиванием один элемент массивакопии: two[0,0]=-size;

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

Программы 07_04.cs и 07_08.cs работают с массивами, у которых по одному спецификатору размерности. В первом случае массив одномерный, во второй программе клонируется двумерный массив. Применяя метод Clone() к массиву массивов, мы сталкиваемся с очень важной особенностью. Строго говоря, действия метода остаются прежними он создаёт массив-копию и присваивает его элементам значения элементов массива-оригинала. Однако, в этом случае копирования тех подчинённых массивов, на которые "смотрят" ссылки-элементы массива-оригинала,

не происходит. Выполняется, так называемое, поверхностное

"копирование:поверхностное копирование" или поразрядное копирование

"копирование:поразрядное копирование" . Иначе и быть не должно – "не знает"

метод Clone(), что код, который является значением элемента массива, представляет собой ссылку, и по этой ссылке нужно ещё что-то "доставать".

Таким образом, копируя с помощью метода Clone() массив массивов, мы получаем два экземпляра массива верхнего уровня, элементы которых адресуют одни и те же участки памяти, выделенные для подчинённых массивов объекта-

оригинала.

В качестве иллюстрации указанной ситуации приведём следующую программу, построенную на основе 07_06.cs:

/07_09.cs - непрямоугольный массив –

//клонирование поверхностное!

using System; class Program

{

static void Main( )

{

int size;

do Console.Write("size = ");

while (!int.TryParse(Console.ReadLine(), out size)||size<1); int[ ][ ] tre = new int[size][ ];

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

{

tre[j] = new int[j + 1]; tre[j][j] = j + 1;

}

Console.WriteLine("Массив tre:");

for (int i = 0; i < tre.Length; i++, Console.WriteLine()) for (int j = 0; j < tre[i].Length; j++)

Console.Write(tre[i][j] + "\t"); Console.WriteLine("tre.Length = " + tre.Length); int[ ][ ] two = (int[ ][ ])tre.Clone( );

two[0][0] = - size; Console.WriteLine("Массив two:");

for (int i = 0; i < two.Length; i++, Console.WriteLine()) for (int j = 0; j < two[i].Length; j++)

Console.Write(two[i][j] + "\t"); Console.WriteLine("Массив tre:");

for (int i = 0; i < tre.Length; i++, Console.WriteLine()) for (int j = 0; j < tre[i].Length; j++)

Console.Write(tre[i][j] + "\t");

}

}

Результат выполнения программы: size = 4<ENTER>

Массив tre:

 

1

2

 

 

0

3

 

0

0

4

0

0

0

tre.Length = 4

 

Массив two:

 

-4

2

 

 

0

3

 

0

0

4

0

0

0

Массив tre:

 

-4

2

 

 

0

3

 

0

0

4

0

0

0

В программе определена ссылка two типа int[ ][ ] и ей присвоен результат копирования (клонирования) "треугольного" массива, адресованного ссылкой tre,

имеющей тип int[ ][ ]. С помощью оператора two[0][0] = - size;

изменён один целочисленный элемент "нижнего уровня" массива массивов. После присваивания изменилось значение, соответствующее выражению tre[0][0].

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