Массив — это последовательность объектов того же типа, которые занимают непрерывную область памяти. Традиционные массивы В стиле C являются источником многих ошибок, но по-прежнему распространены, особенно в старых базах кода. В современном C++ настоятельно рекомендуется использовать std::vector или std::array вместо массивов в стиле C, описанных в этом разделе.
Оба типа стандартных библиотек хранят свои элементы в виде непрерывного блока памяти. Однако они обеспечивают большую безопасность типов и поддерживают итераторы, которые гарантированно указывают на допустимое расположение в последовательности. Дополнительные сведения см. в разделе Контейнеры.
Объявления стека
В объявлении массива C++ размер массива указывается после имени переменной, а не после имени типа, как в некоторых других языках. В следующем примере объявляется массив из 1000 двойников для выделения в стеке. Число элементов должно быть предоставлено в виде целочисленного литерала или в качестве константного выражения. Это связано с тем, что компилятор должен знать, сколько пространства стека необходимо выделить; Он не может использовать значение, вычисленное во время выполнения. Каждому элементу в массиве присваивается значение по умолчанию 0. Если не назначить значение по умолчанию, каждый элемент изначально будет содержать любые случайные значения в этом расположении памяти.
Как найти наибольшее значение в массиве чисел с помощью цикла JavaScript #shorts
constexpr size_t size = 1000; // Declare an array of doubles to be allocated on the stack double numbers[size] ; // Assign a new value to the first element numbers[0] = 1; // Assign a value to each subsequent element // (numbers[1] is the second element in the array.) for (size_t i = 1; i < size; i++) < numbers[i] = numbers[i-1] * 1.1; >// Access each element for (size_t i = 0; i
Первый элемент в массиве — это нулевой элемент. Последним элементом является элемент (n-1), где n — количество элементов, которые может содержать массив. Число элементов в объявлении должно иметь целочисленный тип и быть больше 0. Вы несете ответственность за то, чтобы программа никогда не передает значение оператору индекса, которое больше (size — 1) .
Массив нулевого размера является допустимым только в том случае, если массив является последним полем struct в или union и если расширения Майкрософт включены ( /Za или /permissive- не заданы).
Массивы на основе стека быстрее выделяются и получают к ней доступ, чем массивы на основе кучи. Однако пространство стека ограничено. Количество элементов массива не может быть настолько большим, чтобы использовать слишком много памяти стека. Сколько слишком много зависит от вашей программы. С помощью средств профилирования можно определить, является ли массив слишком большим.
Объявления кучи
Может потребоваться массив, который слишком велик для выделения в стеке или размер которого не известен во время компиляции. Этот массив можно выделить в куче с помощью new[] выражения. Оператор возвращает указатель на первый элемент. Оператор subscript работает с переменной указателя так же, как и в массиве на основе стека. Вы также можете использовать арифметику указателя для перемещения указателя на любые произвольные элементы в массиве. Вы несете ответственность за обеспечение того, чтобы:
Язык C++ с нуля | #31 Поиск элементов массива в c++
- Вы всегда храните копию исходного адреса указателя, чтобы можно было удалить память, когда массив больше не нужен.
- Адрес указателя не увеличивается и не уменьшается за границами массива.
В следующем примере показано, как определить массив в куче во время выполнения. В ней показано, как получить доступ к элементам массива с помощью оператора subscript и с помощью арифметики указателя:
void do_something(size_t size) < // Declare an array of doubles to be allocated on the heap double* numbers = new double[size]< 0 >; // Assign a new value to the first element numbers[0] = 1; // Assign a value to each subsequent element // (numbers[1] is the second element in the array.) for (size_t i = 1; i < size; i++) < numbers[i] = numbers[i — 1] * 1.1; >// Access each element with subscript operator for (size_t i = 0; i < size; i++) < std::cout // Access each element with pointer arithmetic // Use a copy of the pointer for iterating double* p = numbers; for (size_t i = 0; i < size; i++) < // Dereference the pointer, then increment it std::cout // Alternate method: // Reset p to numbers[0]: p = numbers; // Use address of pointer to compute bounds. // The compiler computes size as the number // of elements * (bytes per element). while (p < (numbers + size)) < // Dereference the pointer, then increment it std::cout delete[] numbers; // don’t forget to do this! > int main()
Инициализация массивов
Массив можно инициализировать в цикле, по одному элементу за раз или в одной инструкции. Содержимое следующих двух массивов идентично:
int a[10]; for (int i = 0; i < 10; ++i) < a[i] = i + 1; >int b[10]< 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 >;
Передача массивов в функции
При передаче массива в функцию он передается в качестве указателя на первый элемент, будь то массив на основе стека или на основе кучи. Указатель не содержит других сведений о размере или типе. Такое поведение называется распадом указателя.
При передаче массива в функцию необходимо всегда указывать количество элементов в отдельном параметре. Это поведение также подразумевает, что элементы массива не копируются при передаче массива в функцию. Чтобы функция не изменяла элементы, укажите параметр в качестве указателя на const элементы.
В следующем примере показана функция, которая принимает массив и длину. Указатель указывает на исходный массив, а не копию. Так как параметр не const является , функция может изменять элементы массива.
void process(double *p, const size_t len) < std::cout >
Объявите и определите параметр p массива как const , чтобы сделать его доступным только для чтения в блоке функций:
void process(const double *p, const size_t len);
Эта же функция также может быть объявлена таким образом, без каких-то изменений в поведении. Массив по-прежнему передается в качестве указателя на первый элемент:
// Unsized array void process(const double p[], const size_t len); // Fixed-size array. Length must still be specified explicitly. void process(const double p[1000], const size_t len);
Многомерные массивы
Массивы, созданные из других массивов, являются многомерными. Такие многомерные массивы определяются путем последовательного размещения нескольких константных выражений, заключенных в квадратные скобки. Рассмотрим, например, следующее объявление:
int i2[5][7];
Он задает массив типа int , концептуально упорядоченный в двумерную матрицу из пяти строк и семи столбцов, как показано на следующем рисунке:
Изображение представляет собой сетку шириной 7 ячеек и высотой 5 ячеек. Каждая ячейка содержит индекс ячейки. Первый индекс ячейки помечен как 0,0. Следующая ячейка в этой строке — 0,1 и т. д. до последней ячейки в этой строке, которая имеет значение 0,6. Следующая строка начинается с индекса 1,0. Ячейка после этого имеет индекс 1,1. Последняя ячейка в этой строке — 1,6.
Этот шаблон повторяется до последней строки, которая начинается с индекса 4,0. Индекс последней ячейки в последней строке — 4,6. . image-end
Можно объявить многомерные массивы со списком инициализаторов (как описано в разделе Инициализаторы). В этих объявлениях константное выражение, указывающее границы для первого измерения, можно опустить. Пример:
// arrays2.cpp // compile with: /c const int cMarkets = 4; // Declare a float that represents the transportation costs. double TransportCosts[][cMarkets] = < < 32.19, 47.29, 31.99, 19.11 >, < 11.29, 22.49, 33.47, 17.29 >, < 41.97, 22.09, 9.76, 22.55 >>;
В показанном выше объявлении определяется массив, состоящий из трех строк и четырех столбцов. Строки представляют фабрики, а столбцы — рынки, на которые фабрики поставляют свою продукцию. Значения — это стоимости транспортировки с фабрик на рынки. Первое измерение массива опущено, но компилятор заполняет его, проверяя инициализатор.
Использование оператора косвенного обращения (*) для n-мерного типа массива приводит к получению массива n-1. Если n равно 1, создается скаляр (или элемент массива).
Массивы C++ размещаются в памяти по срокам. Построчный порядок означает, что быстрее всего изменяется последний индекс.
Пример
Можно также опустить спецификацию границ для первого измерения многомерного массива в объявлениях функций, как показано ниже:
// multidimensional_arrays.cpp // compile with: /EHsc // arguments: 3 #include // Includes DBL_MAX #include const int cMkts = 4, cFacts = 2; // Declare a float that represents the transportation costs double TransportCosts[][cMkts] = < < 32.19, 47.29, 31.99, 19.11 >, < 11.29, 22.49, 33.47, 17.29 >, < 41.97, 22.09, 9.76, 22.55 >>; // Calculate size of unspecified dimension const int cFactories = sizeof TransportCosts / sizeof( double[cMkts] ); double FindMinToMkt( int Mkt, double myTransportCosts[][cMkts], int mycFacts); using namespace std; int main( int argc, char *argv[] ) < double MinCost; if (argv[1] == 0) < cout MinCost = FindMinToMkt( *argv[1] — ‘0’, TransportCosts, cFacts); cout double FindMinToMkt(int Mkt, double myTransportCosts[][cMkts], int mycFacts)
The minimum cost to Market 3 is: 17.29
Функция FindMinToMkt написана таким образом, что для добавления новых фабрик не требуется изменение кода, а только перекомпиляция.
Инициализация массивов
Массивы объектов с конструктором класса инициализируются конструктором . Если в списке инициализатора меньше элементов, чем в массиве, для остальных элементов используется конструктор по умолчанию. Если для класса не определен конструктор по умолчанию, список инициализаторов должен быть полным, то есть для каждого элемента массива должен быть один инициализатор.
Рассмотрим класс Point , определяющий два конструктора:
// initializing_arrays1.cpp class Point < public: Point() // Default constructor. < >Point( int, int ) // Construct from two ints < >>; // An array of Point objects can be declared as follows: Point aPoint[3] = < Point( 3, 3 ) // Use int, int constructor. >; int main()
Первый элемент aPoint создается с помощью конструктора Point( int, int ) , а оставшиеся два элемента — с помощью конструктора по умолчанию.
Статические массивы-члены (независимо от того, можно ли const инициализировать) в их определениях (вне объявления класса). Пример:
// initializing_arrays2.cpp class WindowColors < public: static const char *rgszWindowPartList[7]; >; const char *WindowColors::rgszWindowPartList[7] = < «Active Title Bar», «Inactive Title Bar», «Title Bar Text», «Menu Bar», «Menu Bar Text», «Window Background», «Frame» >; int main()
Доступ к элементам массива
К отдельным элементам массива можно обращаться при помощи оператора индекса массива ( [ ] ). Если вы используете имя одномерного массива без индекса, оно вычисляется как указатель на первый элемент массива.
// using_arrays.cpp int main() < char chArray[10]; char *pch = chArray; // Evaluates to a pointer to the first element. char ch = chArray[0]; // Evaluates to the value of the first element. ch = chArray[3]; // Evaluates to the value of the fourth element. >
Если используются многомерные массивы, в выражениях можно использовать различные сочетания.
// using_arrays_2.cpp // compile with: /EHsc /W1 #include using namespace std; int main() < double multi[4][4][3]; // Declare the array. double (*p2multi)[3]; double (*p1multi); cout
В приведенном выше коде multi представляет собой трехмерный массив типа double . Указатель p2multi указывает на массив типа double размера три. В этом примере массив используется с одним, двумя и тремя индексами. Хотя чаще всего указываются все индексы, как в операторе cout , иногда бывает полезно выбрать определенное подмножество элементов массива, как показано в следующих инструкциях cout .
Оператор перегруженного индекса
Как и другие операторы, подстрочный оператор ( [] ) может быть переопределен пользователем. Поведение оператора индекса по умолчанию, если он не перегружен, — совмещать имя массива и индекс с помощью следующего метода.
Как и в случае с другими типами указателей, масштабирование выполняется автоматически для настройки размера типа. Результирующим значением не является n байтов из источника array_name ; вместо этого это n-йэлемент массива. Дополнительные сведения об этом преобразовании см. в разделе Аддитивные операторы.
Аналогично, для многомерных массивов адрес извлекается с использованием следующего метода.
((array_name) + (subscript1 * max2 * max3 * . * maxn) + (subscript2 * max3 * . * maxn) + . + subscriptn))
Массивы в выражениях
Если идентификатор типа массива отображается в выражении, отличном от sizeof , адрес ( Error: Disk drive not ready.»; char *psz = szError1;
Указатель psz указывает на первый элемент массива szError1 . Массивы, в отличие от указателей, не изменяются l-значения. Вот почему следующее назначение является недопустимым:
szError1 = psz;
Источник: learn.microsoft.com
Сколько размерностей массива можно использовать в программах
Каждый массив имеет такую характеристику как размерность. Количество размерностей соотвествует числу пар квадратных скобок. Например:
int numbers[3];
В данном случае массив numbers имеет одну размерность (одна пара квадратных скобок), то есть он одномерный . При этом не важно, сколько элементов содержит этот массив. В любом случае его можно представить в виде ряда элемента значений — в виде строки или столбца.
Но кроме одномерных массивов в C++ есть и многомерные. Элементы таких массивов сами в свою очередь являются массивами, в которых также элементы могут быть массивами. Как правило, распространены двухмерные и трехмерные массивы. Например, определим двухмерный массив чисел:
int numbers[3][2];
Здесь массив numbers имеет две размерности (две пары квадратных скобок): первая размерность равна 3, а вторая размерность — 2. Такой массив состоит из трех элементов, при этом каждый элемент представляет массив из двух элементов. Двухмерный массив еще можно представить в виде таблицы, где первая размерность представляет количество строк, а вторая размерность — количество столбцов.
Подобным образом можно определять массивы и с большим количеством размерностей, например, трехмерный массив:
int numbers[3][2][2];
Как и в общем случае многомерный массив можно инициализировать некоторыми значениями, например, нулями:
int numbers[3][2] <>;
Также можно инициализировать все элементы индивидуальными значениями. Так, массив numbers состоит из трех элементов (строк), каждый из которых представляет массив из двух элементов (столбцов). Поэтому такой массив мы можем проинициализировать, например, следующим образом:
int numbers[3][2] < , // первая строка , // вторая строка // третья строка >;
Вложенные фигурные скобки очерчивают элементы для каждого подмассива. Такой массив еще можно представить в виде таблицы:
1 | 2 |
4 | 5 |
7 | 8 |
Возможна также инициализация не всех элементов, а только некоторых:
int numbers[3][2] < , <>, >;
В этом случае значения присваиваются первым элементам массивов, а остальные элементы инициализируются нулями.
При рассмотрении одномерных массивов мы видели, что компилятор можем автоматически выводить длину массива на основании количества элементов. При инициализации многомерных массивов тоже тоже можно опустить длину массива, но только первую размерность (первые квадратные скобки):
int numbers[][2] < , , >;
Обращение к элементам
И чтобы обратиться к элементам многомерного массива, потребуется индексы для каждой размерности. Так, если речь идет о двухмерном массиве, нам надо указать индексы для обоих его размерностей:
#include int main() < int nums[3][2] < , , >; // получаем значение элемента int n = nums[1][0]; // вторая строка, первый столбец std::cout
Соответственно выражение nums[1][0] представляет обращение к первому элементу второго подмассива (первый столбец второй строки)
Перебор многомерного массива
Переберем двухмерный массив:
#include int main() < const int rows = 3, columns = 2; int numbers[rows][columns] < , , >; for(int i=0; i < rows; i++) < for(int j=0; j < columns; j++) < std::cout std::cout >
Также для перебора элементов многомерного массива можно использовать другую форму цикла for, которая специально предназначена для перебора последовательностей:
#include int main() < const int rows = 3, columns = 2; int numbers[rows][columns] < , , >; for(auto for(int number : subnumbers) < std::cout std::cout >
Для перебора массивов, которые входят в массив, применяются ссылки. То есть во внешнем цикле for(auto subnumbers представляет ссылку на подмассив в массиве. Во внутреннем цикле for(int number : subnumbers) из каждого подмассива в subnumbers получаем отдельные его элементы в переменную number и выводим ее значение на консоль.
Источник: metanit.com
Что такое массив и как он устроен
Заглядываем под капот одной из главных структур данных в программировании.
Иллюстрация: Оля Ежак для Skillbox Media
Иван Стуков
Журналист, изучает Python. Любит разбираться в мелочах, общаться с людьми и понимать их.
В языках программирования информация представлена разными типами данных: целые числа, числа с плавающей точкой, символы, строки, булевы значения и так далее. Благодаря этому компьютер понимает, как хранить информацию и что с ней делать.
Но есть и более сложные сущности, которые называются структурами данных. Они содержат данные, которые, как правило, связаны определённым образом. Одна из таких структур — массивы.
В этой статье мы расскажем, что такое массивы и зачем они нужны. Если же вы хотите научиться работать с ними в коде, почитайте материалы про массивы в C++ и в Java.
Что такое массив
Массив в программировании — это структура данных, которая хранит упорядоченный набор однотипных элементов. Его можно представить в виде шкафчика или камеры хранения на вокзале или в магазине: набор ячеек, в каждой из которых может что-то лежать.
Как и в шкафчике, каждая ячейка массива пронумерована, номер — это её индекс. Причём счёт идёт не от единицы, а от нуля.
Массивы есть в большинстве востребованных языков программирования, часто сразу в нескольких видах. Взгляните на семь самых популярных языков в рейтинге TIOBE — этот тип данных реализован в каждом из них.
При этом в разных языках программирования их могут называть по-разному и даже предлагать несколько реализаций. Например, в Python это lists (списки) и tuples (кортежи), в C++ — arrays (массивы) и vectors (векторы). Всё это массивы, но разных видов.
Для чего нужны массивы
Представьте: мы пишем программу, в которой будет храниться температура окружающей среды по дням за декабрь. Если не использовать массивы, для каждого значения температуры придётся создавать свою переменную.
Эта информация хранится разрозненно, и переменные никак не связаны друг с другом. Если мы захотим посчитать среднюю температуру за декабрь, придётся писать длинную формулу:
А если вдруг окажется, что термометр даёт ошибку в +3 градуса? Чтобы исправить ошибку, придётся вручную отнимать от каждой переменной 3 — и так для каждой из 31 строки кода.
Или, допустим, мы захотим учитывать температуру не только в нашем городе, но и в соседнем. Тогда в названии каждой переменной нужно будет дополнительно указывать не только дату, но и название города — так и запутаться можно.
Гораздо проще пойти другим путём: создать массив «Декабрь» и записывать температуру уже внутри него.
Так программа понимает, что эти элементы связаны между собой. Теперь мы за пару строчек кода можем найти среднемесячную температуру — достаточно сказать программе: сложи все элементы и раздели их на длину массива. Или прибавить три градуса (сказать: прибавь 3 к каждому элементу).
Если захотим измерять температуру в другом городе, можно просто создать другой массив — и их значения точно не перемешаются.
При этом обратиться к температуре любого — легче лёгкого, ведь каждый элемент имеет свой индекс, как номер ячейки в камере хранения. Нужно лишь запомнить, что нумерация обычно идёт от нуля.
Данные хранятся более упорядоченно, нет никакого нагромождения из десятков и сотен переменных, которые нужно держать в голове, чтобы не запутаться и не ошибиться.
В некоторых массивах можно хранить даже другие массивы. Например, мы можем объявить переменную Город_N_2022 и положить в неё массивы каждого из месяцев.
Массивы — очень полезный инструмент, о котором нередко спрашивают на собеседованиях. В разных языках программирования с ними можно совершать множество разных операций. Например, в PHP есть 13 способов для одной только сортировки массивов.
Как устроены массивы
Память в компьютере поделена на ячейки — фрагменты информации, которые имеют свой адрес. Можно сказать, это такой супермассив для всех данных внутри машины.
При создании массива компьютер выделяет непрерывный участок памяти и складывает в него элементы один за другим. Рассмотрим этот процесс подробнее.
Как резервируется память
Когда мы объявляем классический массив, мы также указываем его длину (сколько в нём будет элементов) и тип данных, которые будут в нём храниться. В случае массива «Декабрь» длина будет равна 31, а тип данных — float (число с плавающей запятой — мы всё-таки температуру записываем).
Так как все элементы принадлежат одному типу, они занимают одинаковый объём памяти. Допустим, в нашей архитектуре числа с плавающей запятой весят 4 байта. Компьютер умножает вес одного элемента на длину списка и получает количество памяти, которое ему нужно зарезервировать:
4 * 31 = 124 байта
И вот эти 124 соседних байта резервируются под массив «Декабрь». А до и после них в памяти могут быть какие угодно данные.
Как работает обращение по индексу
Когда мы обращаемся к элементу массива по индексу, компьютеру нужно найти среди всей своей памяти ячейки, в которых лежит нужный элемент.
Для этого он определяет начальный адрес массива. Это адрес ячейки памяти, с которой начинается массив. Если он будет 54921, то память компьютера будет выглядеть следующим образом.
Извлечь из такой структуры элемент с нужным индексом несложно. Это делается по следующей формуле:
начальный адрес массива + индекс * число ячеек, которые занимает один элемент
Для наглядности подставим в эту формулу несколько индексов.
Индекс 0: 54921 + 0 * 4 = 54921 + 0 = 54921
Индекс 1: 54921 + 1 * 4 = 54921 + 4 = 54925
Индекс 2: 54921 + 2 * 4 = 54921 + 8 = 54929
За каждый индекс после нуля адрес ячейки смещается на 4, потому что именно столько весит каждый элемент массива. А если индекс равен нулю, смещения нет. Именно поэтому индексы считают с нуля, а не с единицы.
Теперь становится понятно, почему для классических массивов нужно заранее указывать тип данных и количество элементов:
- Если компьютер не знает тип данных, то он не понимает, сколько памяти нужно выделять под каждый элемент и на сколько ячеек смещаться при поиске элемента по индексу.
- Если компьютер не знает длину массива, то он не понимает, сколько памяти нужно под него выделить.
Виды массивов
Классический массив понятен, предсказуем, занимает мало места и быстро работает, но и возможности его применения ограничены. Чтобы массивы были более гибкими и подходили под разные задачи, в некоторых языках программирования придумали их более сложные реализации.
Статические и динамические
Длина статического массива определена заранее. При его создании программист сразу пишет, сколько в массиве будет элементов. Если положить в него меньше элементов, чем объявлено, то в остальных будут находиться пустые значения.
Это удобно, когда программист сразу знает, сколько элементов ему нужно. Если он записывает среднесуточную температуру за каждый день в декабре, то понадобится 31 элемент, если буквы русского алфавита — 33 элемента.
Длина динамических массивов, напротив, может изменяться во время выполнения программы. Например, программа должна хранить оценки школьника по информатике. Учитель ставит новую оценку — в массив добавляется новый элемент. Так это выглядит со стороны.
На самом деле при добавлении нового элемента компьютер создаёт новый массив, в который копирует старые элементы и добавляет новые. Старый массив при этом удаляется.
Так что по сути динамический массив — это тот же статический, но способный пересоздаваться с другой длиной.
Часто компьютер резервирует под динамические массивы немного больше места, чем нужно при создании. Он делает это для того, чтобы не пришлось пересоздавать массив, когда в него добавляют всего один или два новых элемента.
Однородные и гетерогенные
Однородные массивы состоят из элементов одинакового типа данных. Например, только целые числа или только строки. Хранить элементы разных типов в них нельзя.
В гетерогенных массивах одним элементом может быть целое число, другим — строка, третьим — вообще другой массив. Часто это может быть удобно, но среднее арифметическое всех его элементов, конечно, не посчитаешь. И ладно, если программа выдаст ошибку, — а если действительно решит посчитать и выведет результат?
На самом деле гетерогенные массивы хранят в себе не сами данные, а ссылку на них. Например, питоновский список может выглядеть так: [1, ‘строка’, False]. Но внутреннее его устройство сложнее: [ссылка на 1, ссылка на ‘строка’, ссылка на False]. А сами данные хранятся где-то ещё в разрозненном виде, никак не связанные друг с другом.
Получается, с технической точки зрения гетерогенный массив практически не отличается от гомогенного. Он тоже хранит в себе один тип данных — ссылочный.
Одномерные и многомерные
Одномерный массив — это ряд пронумерованных элементов. Они, как вагоны поезда, следуют друг за другом, и к ним можно обратиться по номеру: первый, второй, третий, десятый.
Двумерный массив — несколько рядов элементов. Его можно представить как таблицу, в которой есть строки и столбцы, а сами элементы находятся на их пересечении. Соответственно, индекс такого массива состоит из двух чисел: номера ряда (столбца) и номера элемента в этом ряду.
С точки зрения реализации он представляет собой массив, в котором лежат одномерные массивы.
Также бывают массивы трёхмерные, четырёхмерные, пятимерные и так далее. Чем больше измерений, тем сложнее структура и тем из большего количества чисел состоят индексы элементов.
Итоги
- Массив — это структура данных фиксированной длины для хранения упорядоченного набора элементов. Элементы при этом должны относиться к одному типу данных.
- Элементы массива физически хранятся в соседних ячейках памяти. К каждому из них можно обратиться по индексу.
- У массива может быть более сложная реализация в зависимости от языка программирования. Длину динамического можно изменять, гетерогенные могут хранить ссылки на разные типы данных, а в многомерных каждый элемент имеет сразу несколько индексов.
- Массивы и работа с ними в С++
- Тест: насколько хорошо вы понимаете время?
- Как установить библиотеку в Python: руководство для новичка
Для удобства будем считать, что ёмкость одной ячейки памяти — 1 байт. В реальности она может быть как больше, так и меньше.
Источник: skillbox.ru