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

Массивы

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

Итак, мы поняли, что массивы на самом деле – синтаксический сахар над указателями. Рассмотрим простейший случай – определим массив целых чисел размером в 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;
}
Также может быть вам интересно:

Указатели, Массивы, Строки

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

Читать »

Строки в Си

Если во многих высокоуровневых языках строки – само собой разумеющееся, то в Си нужно строки знать и уметь ими пользоваться.

Читать »
Фото Как сделать свою middleware в Django (с примерами)

Как сделать свою middleware в Django (с примерами)

Middleware или "промежуточное программное обеспечение" - элегантный способ установить общие правила обработки запросов и ответов приложения. Давайте напишем парочку middleware, чтобы понять, как они работают.

Фото Как настроить отправку почты из Django

Как настроить отправку почты из Django

Письма об ошибках, отчёты на почту, восстановление паролей - всё это полезно при работе с сайтом. Django предоставляет удобный способ это сделать с минимумом настроек!

Фото Добавляем поддержку медиа-файлов в Django проект

Добавляем поддержку медиа-файлов в Django проект

Современные сайты редко ограничиваются только текстом и вёрсткой. Часто в заметках красуются фотографии, а рядом с описанием товаров - их изображения.

Фото Настройка журналирования (логирования) в Python с примерами

Настройка журналирования (логирования) в Python с примерами

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

Фото Нет слов, одни... однострочники

Нет слов, одни... однострочники

На днях вышел пост со списком полезных однострочников для JavaScript программистов. Памятуя Perl-овую молодость, заглянул туда.

Фото Добавляем переменные в контекст Django шаблонов (свой контекст-процессор)

Добавляем переменные в контекст Django шаблонов (свой контекст-процессор)

В Django вы можете передавать данные в шаблоны посредством контекстов. Контекст передаётся из контроллера (view в терминах Django), однако, если одни и те же данные нужны в разных местах, лучше сделать свой контекст-процессор.

Фото Пример своей консольной команды в Django проекте

Пример своей консольной команды в Django проекте

Если вы работали с Django проектом, то, скорее всего, запускали команды из консоли (manage.py). В Django есть простой способ писать свои команды для управления проектом.

Фото Разграничение прав доступа на Django сайте

Разграничение прав доступа на Django сайте

Почти на любом веб-сайте необходимо разделять пользователей на группы и предоставлять им разные возможности. В Django есть довольно серьёзная система прав доступа для пользователей - давайте её рассмотрим!