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

⏱ 25 мин · 🧩 заданий: 5

Массивы

Массив — подряд идущие в памяти элементы одного типа.

int a[5];                       /* 5 int, значения не определены (если локальный) */
int b[5] = {1, 2, 3, 4, 5};
int c[5] = {1, 2};              /* {1, 2, 0, 0, 0} */
int d[] = {7, 8, 9};            /* размер 3 вычислен по инициализатору */
double z[70] = {0};             /* все нули */

size_t n = sizeof(b) / sizeof(b[0]);   /* число элементов: 5 */

Индексы начинаются с нуля: у массива из N элементов допустимы индексы 0…N−1.

int b[5]: адрес b = 0x2000 ┌───────┬───────┬───────┬───────┬───────┐ │ b[0] │ b[1] │ b[2] │ b[3] │ b[4] │ │ 1 │ 2 │ 3 │ 4 │ 5 │ └───────┴───────┴───────┴───────┴───────┘ 0x2000 0x2004 0x2008 0x200C 0x2010

Нет проверки границ

b[5] и b[-1] компилируются без ошибок. Это главный источник уязвимостей в программах на C (переполнение буфера).

Массивы и функции

Массив в функцию передаётся как указатель на первый элемент. Поэтому размер передают отдельно:

double dot(const double *a, const double *b, int n)
{
    double s = 0.0;
    for (int i = 0; i < n; i++)
        s += a[i] * b[i];
    return s;
}

Запись double a[] в параметре — то же самое, что double *a.

Массивы и страницы памяти

Раз элементы лежат подряд, массив может пересекать границу страницы. Посчитаем на примере задачи 7: 70 элементов double = 560 байт, страница 512 байт.

Где начинается массив Сколько страниц занимает
ровно с начала страницы 2 (512 + 48 байт)
со смещения 504 (последний double страницы) 3 (8 + 512 + 40 байт)

Минимум страниц для N байт — ⌈N / P⌉, максимум — ⌈N / P⌉ + 1 (если массив «неудачно» начинается в конце страницы). Это ключ к поиску минимального и максимального ответа.

Двумерные массивы

int m[3][4];          /* 3 строки по 4 элемента, хранятся построчно */
m[1][2] = 5;          /* адрес: m + (1*4 + 2) * sizeof(int) */

Обход по строкам (for i { for j { m[i][j] } }) идёт по памяти подряд — быстро. Обход по столбцам прыгает через строку — для больших массивов это много промахов кеша и TLB.

Строки

Строка в C — массив char, заканчивающийся нулевым байтом '\0'.

char s[] = "fork";          /* {'f','o','r','k','\0'} — 5 байт */
const char *p = "exec";     /* указатель на строковую константу (только чтение!) */

printf("%s %zu %zu\n", s, sizeof(s), strlen(s));   /* fork 5 4 */

Функции из <string.h>:

Функция Действие
strlen(s) длина без '\0'
strcpy(dst, src) / strncpy копирование (опасно без контроля размера)
snprintf(buf, size, fmt, ...) безопасная форматированная запись в буфер
strcmp(a, b) сравнение: 0 — равны
strcat, strchr, strstr склейка, поиск символа, поиск подстроки
memcpy, memset, memcmp работа с произвольными байтами

Сравнение строк

if (s == "fork") сравнивает адреса, а не содержимое. Правильно: if (strcmp(s, "fork") == 0).

Почему строковую константу нельзя менять?

Литерал "exec" компоновщик кладёт в сегмент только для чтения (.rodata). Страницы этого сегмента отображены без права записи, поэтому p[0] = 'E' вызовет страничное исключение защиты, и ядро пошлёт процессу SIGSEGV. Массив char s[] = "fork" — копия литерала в стеке, его менять можно.

Итог

  • Массив — элементы подряд; индексы с 0; границы не проверяются.
  • В функцию массив передаётся как указатель, размер — отдельно.
  • Массив размера N байт занимает ⌈N/P⌉ или ⌈N/P⌉+1 страниц размера P.
  • Строка — массив char с завершающим '\0'; сравнение — strcmp.

Практика решено 0 из 5

Вы не вошли — ответы проверяются, но прогресс не сохранится. Войти или зарегистрироваться.
Задание 1 Ввод ответа

Сколько байт занимает строка char s[] = "fork";?

Задание 2 Ввод ответа

Что напечатает фрагмент?

int a[5] = {1, 2};
printf("%d %d\n", a[1], a[4]);
Задание 3 Написать код

Напишите функцию double sum(const double *a, int n), возвращающую сумму n элементов массива.

Задание 4 Один вариант ответа

Что происходит при обращении к a[10], если массив объявлен как int a[10]?

Задание 5 Несколько вариантов

Какие утверждения о массиве-параметре void f(int a[]) верны?

Войдите, чтобы отмечать пройденные уроки.