Программы
Массивы

Массивы

Массивы в Си – довольно интересная структура данных. Простая и эффективная как топор!

Итак, мы поняли, что массивы на самом деле – синтаксический сахар над указателями. Рассмотрим простейший случай – определим массив целых чисел размером в 10 элементов.

    int nums[10];
    nums[0] = 0;
    nums[1] = 1;
    nums[2] = 4;
    nums[3] = 9;
    nums[4] = 16;

– как и во многих других языках, адресация в массиве начинается с нуля – сдвиг от адреса начала.

    int nums[10] = {
        0, 1, 4, 9, 16, 25, 36, 49, 64, 81
    };
  • Так можно инициализировать массив при создании.
Изображение Изучаем язык программирования Си

Массивы и циклы

Часто приятнее использовать циклы для работы с массивом:

#include <stdio.h>

int main(void) {
    int nums[10];

    for (int i = 0; i < 10; ++i) {
	// Заполнили квадратами
        nums[i] = i * i;
    }

    for (int i = 0; i < 10; ++i) {
	// Выводим всё по одному
        printf("%d\n", nums[i]);
    }

    return 0;
}

Чему равен nums[10]?

Для разминки, то же самое на указателях:

#include <stdio.h>

int main(void) {
    int nums[10];

    for (
	// Создаём и инициализируем указатель адресом начала массива, инициализируем i нулём.
	// С помощью запятой можно несколько операторов объединить синтаксически в один.
	int *ptr = nums, i = 0;
	// Останавливаемся, когда указатель перейдёт за границы массива.
	ptr < nums + 10;
	// На каждом шаге двигаемся вперёд на один элемент
	// (помните, как работает арифметика указателей?)
	++ptr, ++i
    ) {
        *ptr = i * i;  // Кладём по адресу ptr значение i*i
    }

    for (int *ptr = nums; ptr < nums + 10; ++ptr) {
        printf("%d\n", *ptr);
    }

    return 0;
}

Многоуровневые массивы

Мы рассмотрели одноуровневые / одномерные массивы. Часто этого хватает, но, например, в случае определения 2D пространства нам этого не хватит.

Как и многое в Си, многомерные массивы устроены крайне просто (об этом чуть позже) и поддерживают довольного удобный способ доступа:

#include <stdio.h>

int main(void) {
    int nums[4][10];

    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 10; ++j) {
            nums[i][j] = 1;

            if (i > 0) {
                for (int k = 0; k < i; ++k) {
                    nums[i][j] *= j;
                }
            }
        }
    }

    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 10; ++j) {
            printf("%d ", nums[i][j]);
        }
        printf("\n");
    }

    return 0;
}

Или на указателях:

#include <stdio.h>

int main(void) {
    int nums[4][10];

    for (int i = 0; i < 4; ++i) {
        for (int j = 0; j < 10; ++j) {
            nums[i][j] = 1;

            if (i > 0) {
                for (int k = 0; k < i; ++k) {
                    nums[i][j] *= j;
                }
            }
        }
    }

    // Объявляем указатель на массив из 10 элементов.
    // То есть по сути - указатель на указатель.
    for (int (*i)[10] = nums; i != nums + 4; ++i) {
        for (int *j = *i; j != *i + 10; ++j) {
            printf("%d ", *j);
        }
        printf("\n");
    }

    // Такой способ также работает благодаря устройству многомерных массивов в Си
    for (int *ptr = &nums[0][0], i = 0; i < 4 * 10; ++i) {
        printf("%d ", *(ptr + i));

        if (i % 10 == 9) {
            printf("\n");
        }
    }
}

– уровни массива располагаются один за другим. То есть после nums[0][9] следует nums[1][0].

Небольшая пояснительная схема:

+-----------------+-----------------+-----------------+-----------------+
| [0][0] - [0][9] | [1][0] - [1][9] | [2][0] - [2][9] | [3][0] - [3][9] |
+-----------------+-----------------+-----------------+-----------------+

– данные массива nums расположены элемент за элементом одним непрерывным куском памяти. Именно благодаря этому мы смогли так ловко на одном указателе пройти весь двумерный массив.

Ещё одно небольшое объяснение (запутывание):

*(*ptr+1) = *(*(ptr + 0) + 1 ) = *(ptr[0] + 1) = ptr[0][1]

nums[0][0] = *(*(nums))
nums[i][j] = *((*(nums)) + (i * COLS + j))
nums[i][j] = *(*(nums + i) + j)
nums[i][j] = *(nums[i] + j)
nums[i][j] = (*(nums + i))[j]
&nums[i][j] = ((*(nums)) + (i * COLS + j))

И для закрепления рассмотрим 3d массив:

#include<stdio.h>

int main() {
  int arr[2][3][2] = {
     {
       {5, 10},
       {6, 11},
       {7, 12},
     },
     {
       {20, 30},
       {21, 31},
       {22, 32},
     }
  };

  int i, j, k;
  for (i = 0; i < 2; i++) {
    for (j = 0; j < 3; j++) {
       for (k = 0; k < 2; k++) {
         printf("%d\t", *(*(*(arr + i) + j) +k));
       }

       printf("\n");
    }
  }

  for (i = 0; i < 2 * 3 * 2; i++) {
    printf("%d ", *(**arr + i));
  }
  printf("\n");
  return 0;
}