JavaRush /Курсы /C# SELF /Продвинутая работа с массивами

Продвинутая работа с массивами

C# SELF
7 уровень , 2 лекция
Открыта

1. Массив типа string

Ну и вкратце хотелось бы рассказать о массиве типа string.

Как мы уже говорили, массив может быть любого типа. А значит, можно создать массив типа string. Вот как бы выглядел код, если бы нам нужно было написать программу, которая «вводит с клавиатуры 10 строк и выводит их на экран в обратном порядке».

string[] array = new string[10];	// Создаем объект-массив на 10 элементов
for (int i = 0; i < 10; i++)		// Цикл от 0 до 9
{
   array[i] = Console.ReadLine();	// Читаем строку с клавиатуры и сохраняем ее в ячейку массива
}
for (int i = 9; i >= 0; i--)		// Цикл от 9 до 0
{
    Console.WriteLine(array[i]);	// Выводим на экран очередную ячейку массива
}

Код практически не изменился! Пришлось только при создании массива заменить тип intна string. В остальном все абсолютно то же самое!

2. Массив типа string в памяти

И еще один полезный факт. Рассмотрим 3 картинки:

Картинка 1. Как объект string располагается в памяти:

Массив типа string в памяти

Обращаем ваше внимание, что текст строки не хранится прямо в переменной: для него выделяется отдельный блок памяти. А в переменной типа string хранится адрес (ссылка) на объект с текстом.

Картинка 2. Как массив целых чисел располагается в памяти:

Массив int в памяти

Эту картинку вы также видели.

Картинка 3. Как в памяти располагается массив строк:

Как в памяти располагается string массив

Слева мы видим переменную-массив типа string[] (хранит адрес объекта-массива).
В середине — объект-массив типа string.
А справа — объекты-строки, которые хранят какие-то тексты.

В ячейках объекта-массива типа string хранятся не сами строки (тексты), а их адреса (ссылки). Точно так же, как в переменных типа string хранятся адреса строк (текста).

3. Быстрая инициализация массива в C#

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

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

int[] months = new int[12];
months[0] = 31; // январь
months[1] = 28; // февраль
months[2] = 31; // март
months[3] = 30; // апрель
months[4] = 31; // май
months[5] = 30; // июнь
months[6] = 31; // июль
months[7] = 31; // август
months[8] = 30; // сентябрь
months[9] = 31; // октябрь
months[10] = 30; // ноябрь
months[11] = 31; // декабрь

Но есть способ записать его короче — спасибо создателям C#:

// длины месяцев года
int[] months = new int[] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

Можно просто перечислить через запятую все значения массива!

Удобно, да? Но и это еще не все.

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

Поэтому этот код можно записать еще короче:

// длины месяцев года
int[] months = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

Разве не красота? 🙂

Такая запись называется «быстрая инициализация массива». Она, кстати, работает не только для типа int...

// названия месяцев года
string[] months = { "Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"};

4. Цикл foreach

Проход по всем элементам массива настолько частая операция, что для нее придумали отдельный цикл(!). Он называется foreach. И записывается вот так:


foreach (int score in scores)
{
    Console.WriteLine($"Баллы: {score}");
}
        
Перебор элементов массива с помощью foreach

Здесь цикл сам перебирает элементы ― никакого ручного индекса. Нельзя менять элементы напрямую (они доступны только для чтения в цикле), зато очень удобно просто "пройтись" по массиву.

Компилятор превратит код цикла foreach в код ниже:


for (int i = 0; i < scores.Length; i++)
{
    int score = scores[i];
    Console.WriteLine($"Баллы: {score}");
}
        
Как работает foreach "под капотом"

Так что никакой магии. Зато теперь становится понятно, почему вы не можете изменить элементы массива через переменную score.

Давайте напишем еще пример: найдем сумму всех баллов.


int sum = 0;
foreach (int score in scores)
{
    sum += score;
}
Console.WriteLine($"Сумма всех баллов: {sum}");
        
Суммирование элементов массива с помощью foreach

Когда использовать какой цикл?

  • Если нужен индекс или требуется изменять элементы — цикл for.
  • Если просто пройтись по значениям — foreach быстрее и чище.

5. Индексирование с конца: оператор ^

С некоторых пор в C# можно получать элементы с конца массива. Раньше ведь как было, нужен вам последний элемент массива — пишете что-то типа:


int lastScore = scores[scores.Length - 1];
        
Получение последнего элемента массива по старинке

Теперь все гораздо проще. Нужно просто перед индексом написать оператор ^, и счет пойдет с конца:


int lastScore = scores[^1];        // Последний элемент
int penultimate = scores[^2];      // Предпоследний элемент
        
Индексирование с конца с помощью ^
  • ^1 — это первый с конца (последний).
  • ^2 — второй с конца.

Визуализация

Индекс Значение в массиве scores Обычный индекс Индекс с конца
0 10 0 ^5
1 15 1 ^4
2 8 2 ^3
3 22 3 ^2
4 17 4 ^1

score[^1] = 17, score[^2] = 22 — легко и понятно.

Важно! Обратите внимание, что индексы с конца нумеруются с 1, не с нуля.

6. Диапазоны: оператор .. (range)

Иногда нужно получить подмассив из исходного массива. В C# для этого можно использовать специальный оператор диапазона .. — это лаконичный и очень удобный способ получить нужный "кусочек" массива.

Такой подмассив иногда называют срезом (от английского "slice"), и он определяется двумя границами: началом и концом.
Важный момент: правая граница не включается в результат!

Как это выглядит


int[] scores = { 10, 15, 8, 22, 17 };
int[] top3 = scores[0..3]; // Берём элементы с 0 по 2: {10, 15, 8}
        
Получение среза массива с помощью ..

  • scores[0..3] — берём элементы с индекса 0 (включительно) до индекса 3 (НЕ включительно).
  • Итоговый массив будет содержать элементы с индексами 0, 1 и 2.

Сокращения

Если левая граница не указана, берём с самого начала:


int[] firstTwo = scores[..2]; // то же самое, что scores[0..2], то есть {10, 15}
        

Если правая граница не указана, берём до самого конца:


int[] fromThird = scores[2..]; // {8, 22, 17}
        

Отрицательные индексы

С помощью символа ^ можно отсчитывать индексы с конца:


int[] lastTwo = scores[^2..]; // {22, 17}
        

Здесь ^2 — это "второй элемент с конца".

Можно также брать диапазон "от и до", используя отрицательные индексы с обеих сторон:


int[] mid = scores[1..^1]; // {15, 8, 22} (со 2-го до предпоследнего)
        

Что важно помнить

  • Правый край всегда не включается — то есть если пишем a..b, получаем все элементы от a до b-1.
  • Если указать границы, выходящие за пределы массива, будет выброшено исключение IndexOutOfRangeException.
  • Срез — это новый массив, оригинал не меняется. Любые изменения в новом массиве не затронут исходный.

Практические примеры

int[] arr = { 1, 2, 3, 4, 5, 6, 7 };
int[] arr = { 1, 2, 3, 4, 5, 6, 7 };

int[] start = arr[..3];      // {1, 2, 3}
int[] end = arr[^2..];       // {6, 7}
int[] middle = arr[2..5];    // {3, 4, 5}
int[] all = arr[..];         // {1, 2, 3, 4, 5, 6, 7} (полная копия)
2
Задача
C# SELF, 7 уровень, 2 лекция
Недоступна
Использование индексирования с конца и диапазонов
Использование индексирования с конца и диапазонов
2
Задача
C# SELF, 7 уровень, 2 лекция
Недоступна
Реверс массива строк и срезы
Реверс массива строк и срезы
Комментарии (4)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Дмитрий Уровень 60
16 августа 2025
Для решения первой задачи нужно сначала узнать, что вывод массива через Console.WriteLine сразу невозможен. Нужно сформировать строку из массива с разделителями - например, с помощью string.Join. Про это в лекциях не было - нужно гуглить(но похожие ситуации и в курсе Java)
Dom_Tal Уровень 16
3 сентября 2025
Если заморочиться, то можно и без string.Join.
Andrey Belyakov Уровень 12
19 ноября 2025
Мне кажется главное фантазия, многое можно сделать проще благодаря готовым методам и соглашусь, что частично бывают задачи где почти не реально без гугла дойти, но конкретно в этой у меня получилось без string.Join Console.Write($"Последние три элемента: "); foreach (var i in lastThree) { if (i == (lastThree[^1])) { Console.Write(i); } else Console.Write(i + ", "); }
Mark Marchenko Уровень 27
5 января 2026
В условии строго указано 3, и поэтому обобщать до кода который одинаково хорошо работает и с тремя и 300 элементами - не стоит. Можно просто

int[] subArray = array[^3..];
Console.WriteLine($"Последние три элемента: {subArray[0]}, {subArray[1]}, {subArray[2]}");