Неделя 4 · Foundation

Массивы и строки

На прошлой неделе одна переменная хранила одно значение. Теперь научимся хранить несколько значений под одним именем — то есть массивы. Затем рассмотрим строки: в C строка на самом деле массив из символов. Каждую идею мы шаг за шагом разберём в живом визуализаторе массивов и отладчике кода в стиле IDE.

Что вы узнаете на этом уроке

Создадите массив и сохраните много значений под одним именем
Обратитесь к каждому элементу по индексу, начиная с 0
Пройдёте циклом по всему массиву и посчитаете сумму и среднее
Поймёте, что строка — это массив из char, и что такое нулевой терминатор (\0)
Посчитаете длину строки с помощью strlen
Переберёте символы строки по одному и измените их

4.1 Что такое массив?

Представьте, что вам нужно сохранить оценки 5 учеников одного класса. Со знаниями прошлой недели вы написали бы пять отдельных переменных: ball1, ball2, ball3 и так далее. Для 5 это ещё терпимо, но что, если учеников 30 или 100?

Именно эту задачу решает массив (array). Под массивом понимается хранение нескольких значений одного типа под одним именем. Пять оценок теперь стоят рядом под одним именем ballar:

ballar.c
1// вместо 5 отдельных переменных
2int ballar[5] = {90, 85, 70, 88, 95};

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

  • Все элементы одного типа, например все int
  • Количество элементов задаётся заранее и потом не меняется, здесь их 5
Представьте массив как ряд ячеек в памяти: стоящие рядом одинаковые ячейки, в каждой по одному значению. Этот ряд — почти та же идея, что и список (list) в Scratch.

4.2 Создание и индекс

При создании массива в квадратных скобках мы пишем, сколько в нём элементов, а в фигурных скобках — сами значения. Теперь самая важная идея: у каждого элемента есть свой индекс (номер), и счёт начинается не с 1, а с 0.

Значит, в массиве из 5 элементов индексы будут 0, 1, 2, 3, 4. Первый элемент — ballar[0], а последний — ballar[4]. Нажмите на ячейку ниже:

Визуализатор массива нажмите на ячейку
Нажмите на одну из ячеек выше: вы увидите её индекс и значение.
Поскольку индекс начинается с 0, последний индекс всегда равен длина минус один. В массиве из 5 элементов последний индекс — 4. Если вы напишете ballar[5], вы выйдете за границы массива, а это считается серьёзной ошибкой.

4.3 Чтение и изменение

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

main.c
1int x = ballar[1]; // чтение: x теперь 85
2ballar[1] = 100; // изменение: вместо 85 теперь 100

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

Изменение элемента выберите индекс
ballar[
] =
amal.c
1ballar[0] = 100;
Выберите индекс, введите значение и нажмите кнопку «Установить».

4.4 Цикл по массиву

Настоящая сила массива раскрывается вместе с циклом. Вместо того чтобы писать каждый элемент вручную, мы циклом for проводим индекс от 0 до конца и обращаемся к каждой ячейке по очереди. Программа ниже складывает все оценки и выводит сумму.

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

Отладчик кода (debugger) пройдите по шагам
ballar.c
terminal

Сумма получилась 428. Чтобы найти среднее, делим её на количество элементов: 428 / 5. По тому же шаблону мы можем посчитать и наибольшее, и наименьшее, и сколько из них прошло.

Сделайте прогноз

Что эта программа выведет в терминал?

main.c
1int a[4] = {2, 4, 6, 8};
2printf("%d", a[1] + a[3]);
10
12
6

4.5 Строки (string)

Теперь переходим к буквам. На прошлой неделе мы видели, что тип char хранит одну букву. Но слово, имя или предложение состоят из множества букв. Чтобы хранить их, мы используем строку (string).

Самая важная идея вот в чём: в C строка на самом деле массив из char. То есть строка такая же, как массив, только внутри неё не числа, а буквы. У каждой буквы есть свой индекс, и счёт снова начинается с 0:

ism.c
1char ism[] = "Ali";
2printf("%s\n", ism); // %s для строки

Введите слово и посмотрите, на каком индексе стоит каждая его буква:

Визуализатор строки введите слово
char so'z[] =

4.6 Нулевой терминатор (\0)

Здесь есть тонкое, но очень важное правило C. Как компьютер узнаёт, где строка в памяти заканчивается? Для этого C ставит в конец каждой строки невидимый особый символ: нулевой терминатор, то есть \0. Он означает «строка заканчивается здесь».

Значит, хотя "Ali" — три буквы, в памяти оно занимает четыре ячейки: A, l, i и в конце \0. Введите слово ниже и обратите внимание на ячейку \0 в конце:

Визуализатор нулевого терминатора введите слово
char ism[] =
Поэтому для слова из n букв нужно как минимум n плюс 1 ячеек. Если вы сами задаёте размер массива, не забудьте оставить место для \0. Если же вы пишете в кавычках "Ali", C делает это автоматически.

4.7 strlen: длина строки

Часто нам нужно знать, из скольких букв состоит строка. Мы не считаем это вручную — есть готовая функция: strlen (string length, то есть длина строки). Она идёт от начала строки, считает буквы и останавливается, дойдя до \0.

Эта функция находится в библиотеке string.h, поэтому в начале программы нужно написать #include <string.h>. Введите слово ниже и нажмите кнопку Посчитать, понаблюдайте, как strlen считает:

Счётчик strlen Посчитать
длина: 0
Внимание: strlen считает только настоящие буквы и не учитывает \0. Поэтому результат strlen("Ali") равен 3, хотя в памяти 4 ячейки.

4.8 Перебор символов

Поскольку строка — это массив, по ней тоже можно пройти циклом. Но здесь есть красивый приём: нам необязательно знать длину заранее, мы просто идём, пока не дойдём до \0. Условие цикла как раз это и проверяет: ism[i] != '\0'.

Например, превратим каждую строчную букву в заглавную. В таблице ASCII строчная 'a' имеет код 97, а заглавная 'A' — 65, то есть разница между ними равна 32. Значит, если вычесть из строчной буквы 32, получится заглавная. Нажмите кнопку Выполнить:

Преобразование в заглавные Выполнить
terminal

4.9 Практика: поиск наибольшего элемента

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

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

Поиск наибольшего элемента шагайте
eng_katta.c
1int max = ballar[0];
2for (int i = 1; i < 5; i++)
3 if (ballar[i] > max)
4 max = ballar[i];

4.10 Глубже advanced

Вы освоили основы массивов и строк. Теперь кратко взглянем ещё на два расширения: изучать их полностью сейчас необязательно, достаточно общего представления.

Двумерный массив (матрица)

Массив мы представляли как ряд. Если ячейки располагаются не только по строке, но и по столбцу, получается двумерный массив: это таблица или матрица. Например, так устроены шахматная доска или лист Excel.

jadval.c
1int jadval[2][3] = {
2 {1, 2, 3}, // строка 0
3 {4, 5, 6} // строка 1
4};
5printf("%d\n", jadval[1][2]); // 6

Теперь элемент берётся по двум индексам: jadval[строка][столбец]. Выше jadval[1][2] даёт значение в строке 1, столбце 2, то есть 6.

Массив строк

Подобно тому как строка — это массив из char, несколько строк тоже можно собрать в один массив. Это удобно, например, для хранения дней недели или списка имён:

kunlar.c
1char *kunlar[] = {"Du", "Se", "Cho"};
2printf("%s\n", kunlar[0]); // Du
Знак * выше означает pointer, то есть указатель. Он работает с адресом в памяти, и мы отдельно и глубоко изучим его на следующих неделях. Пока достаточно понимать это как «в каждой ячейке стоит одна строка».

Словарь терминов

массивнабор значений одного типа под одним именем.
индексномер элемента, начинается с 0.
элементодна ячейка массива и значение в ней.
строкапоследовательность букв, на самом деле массив из char.
\0нулевой терминатор, особый символ, обозначающий конец строки.
strlenфункция, вычисляющая длину строки (число букв).

4.11 Тест знаний

16 вопросов. Чтобы завершить неделю, ответьте правильно как минимум на 11 из них.

Поздравляем! Неделя 4 завершена

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

Следующая неделя: Алгоритмы (линейный и двоичный поиск, сортировка, эффективность Big-O).

Перейти к следующему модулю