0 added
0 removed
Original
2026-01-01
Modified
2026-02-26
1
<p>Работая на таких высокоуровневых языках, как JavaScript, позволительно не знать устройство массивов для решения повседневных задач. С другой стороны, подобное понимание делает код менее магическим и дает возможность заглядывать чуть дальше.</p>
1
<p>Работая на таких высокоуровневых языках, как JavaScript, позволительно не знать устройство массивов для решения повседневных задач. С другой стороны, подобное понимание делает код менее магическим и дает возможность заглядывать чуть дальше.</p>
2
<h2>Массивы в Cи</h2>
2
<h2>Массивы в Cи</h2>
3
<p>Реальные массивы лучше всего рассматривать на языке<strong>Cи</strong>, который, с одной стороны, достаточно простой и понятный, с другой - очень близок к железу и не скрывает от нас практически ничего. Когда мы говорим про примитивные типы данных, такие как "строка" или "число", то на интуитивном уровне все довольно понятно: под каждое значение выделяется некоторый размер памяти в соответствии с типом, в которой и хранится само значение. А как должна выделиться память под хранение массива? И что такое массив в памяти? На уровне хранения понятия массив не существует. Массив представляется цельным куском памяти, размер которого вычисляется по следующей формуле:<em>количество элементов * количество памяти под каждый элемент</em>. Из этого утверждения есть два интересных вывода:</p>
3
<p>Реальные массивы лучше всего рассматривать на языке<strong>Cи</strong>, который, с одной стороны, достаточно простой и понятный, с другой - очень близок к железу и не скрывает от нас практически ничего. Когда мы говорим про примитивные типы данных, такие как "строка" или "число", то на интуитивном уровне все довольно понятно: под каждое значение выделяется некоторый размер памяти в соответствии с типом, в которой и хранится само значение. А как должна выделиться память под хранение массива? И что такое массив в памяти? На уровне хранения понятия массив не существует. Массив представляется цельным куском памяти, размер которого вычисляется по следующей формуле:<em>количество элементов * количество памяти под каждый элемент</em>. Из этого утверждения есть два интересных вывода:</p>
4
<ul><li>Размер массива - фиксированная величина. Те динамические массивы (изменяющие свой размер во время работы), с которыми мы имеем дело во многих языках, реализованы уже внутри языка, а не на уровне железа.</li>
4
<ul><li>Размер массива - фиксированная величина. Те динамические массивы (изменяющие свой размер во время работы), с которыми мы имеем дело во многих языках, реализованы уже внутри языка, а не на уровне железа.</li>
5
<li>Все элементы массива имеют один тип и занимают одно и то же количество памяти. Благодаря этому появляется возможность простым умножением (по формуле, описанной выше) получить адрес той ячейки, в которой лежит нужный нам элемент. Именно это происходит под капотом, при обращении к элементу массива под определенным индексом.</li>
5
<li>Все элементы массива имеют один тип и занимают одно и то же количество памяти. Благодаря этому появляется возможность простым умножением (по формуле, описанной выше) получить адрес той ячейки, в которой лежит нужный нам элемент. Именно это происходит под капотом, при обращении к элементу массива под определенным индексом.</li>
6
</ul><p>Фактически индекс в массиве - смещение относительно начала куска памяти, содержащего данные массива. Адрес, по которому расположен элемент под конкретным индексом, рассчитывается так:<em>индекс * количество памяти, занимаемое одним элементом (для данного типа данных на данной архитектуре)</em>. Пример на Си:</p>
6
</ul><p>Фактически индекс в массиве - смещение относительно начала куска памяти, содержащего данные массива. Адрес, по которому расположен элемент под конкретным индексом, рассчитывается так:<em>индекс * количество памяти, занимаемое одним элементом (для данного типа данных на данной архитектуре)</em>. Пример на Си:</p>
7
<p>Если предположить, что тип int занимает в памяти 2 байта, то адрес элемента, соответствующего индексу 3, вычисляется так:<em>начальный адрес + 3 * 2</em>. Начальный адрес - это адрес ячейки памяти, начиная с которой располагается массив. Он формируется во время выделения памяти под массив. Ниже пример расчета адресов памяти под разные элементы массива numbers:</p>
7
<p>Если предположить, что тип int занимает в памяти 2 байта, то адрес элемента, соответствующего индексу 3, вычисляется так:<em>начальный адрес + 3 * 2</em>. Начальный адрес - это адрес ячейки памяти, начиная с которой располагается массив. Он формируется во время выделения памяти под массив. Ниже пример расчета адресов памяти под разные элементы массива numbers:</p>
8
<p>Теперь должно быть понятно, почему индексы в массиве начинаются с нуля. 0 - означает отсутствие<strong>смещения</strong>.</p>
8
<p>Теперь должно быть понятно, почему индексы в массиве начинаются с нуля. 0 - означает отсутствие<strong>смещения</strong>.</p>
9
<p>Но не все данные имеют одинаковый размер. Как будет храниться массив строк? Строки имеют разную длину и требуют разное количество памяти для своего хранения. Один из способов сохранить строки в массиве на языке Си - создать массив массивов (тут нужно понимать, что любая строка в Си это массив символов). Вложенные массивы обязательно должны быть одного размера, невозможно обойти физические ограничения массивов. Хитрость в том, что этот размер должен быть достаточно большой, чтобы туда поместились необходимые строки.</p>
9
<p>Но не все данные имеют одинаковый размер. Как будет храниться массив строк? Строки имеют разную длину и требуют разное количество памяти для своего хранения. Один из способов сохранить строки в массиве на языке Си - создать массив массивов (тут нужно понимать, что любая строка в Си это массив символов). Вложенные массивы обязательно должны быть одного размера, невозможно обойти физические ограничения массивов. Хитрость в том, что этот размер должен быть достаточно большой, чтобы туда поместились необходимые строки.</p>
10
<h3>Безопасность</h3>
10
<h3>Безопасность</h3>
11
<p>В отличие от высокоуровневых языков, в которых код защищен от выхода за границу массива, в таком языке, как<strong>C</strong>, выход за границу не приводит к ошибкам (на самом деле он может приводить к segfault, но это здесь не важно). Обращение к элементу, индекс которого находится за пределами массива, вернет данные, которые лежат в той самой области памяти, куда его попросили обратиться в соответствии с формулой выше. Чем они окажутся - никому не известно (но они будут проинтерпретированы в соответствии с типом массива. Если массив имеет тип int, то вернется число). Выход за границу массива активно эксплуатируется хакерами для взлома программ.</p>
11
<p>В отличие от высокоуровневых языков, в которых код защищен от выхода за границу массива, в таком языке, как<strong>C</strong>, выход за границу не приводит к ошибкам (на самом деле он может приводить к segfault, но это здесь не важно). Обращение к элементу, индекс которого находится за пределами массива, вернет данные, которые лежат в той самой области памяти, куда его попросили обратиться в соответствии с формулой выше. Чем они окажутся - никому не известно (но они будут проинтерпретированы в соответствии с типом массива. Если массив имеет тип int, то вернется число). Выход за границу массива активно эксплуатируется хакерами для взлома программ.</p>
12
<h2>Массивы в динамических языках</h2>
12
<h2>Массивы в динамических языках</h2>
13
<p>В динамических языках, таких как JavaScript, устройство массивов значительно сложнее, чем в си. Так как типы данных вычисляются автоматически во время выполнения кода. Массив в такой среде не может работать, как в языке C. Неизвестно, данные каких типов окажутся внутри в процессе работы.</p>
13
<p>В динамических языках, таких как JavaScript, устройство массивов значительно сложнее, чем в си. Так как типы данных вычисляются автоматически во время выполнения кода. Массив в такой среде не может работать, как в языке C. Неизвестно, данные каких типов окажутся внутри в процессе работы.</p>
14
<p>Массивы в таких языках содержат не сами данные, а ссылки (адреса в памяти) на них. Тогда становится не важно, что хранить. Любое значение в массиве - адрес, имеющий одинаковый размер независимо от данных, на которые он указывает. Такой подход делает массивы гибкими, но с другой стороны, более медленными.</p>
14
<p>Массивы в таких языках содержат не сами данные, а ссылки (адреса в памяти) на них. Тогда становится не важно, что хранить. Любое значение в массиве - адрес, имеющий одинаковый размер независимо от данных, на которые он указывает. Такой подход делает массивы гибкими, но с другой стороны, более медленными.</p>
15
<p>Кроме того, массивы в динамических языках тоже динамические. То есть их размер может увеличиваться или уменьшаться в процессе работы программы. Технически это работает так: если ссылки (помним, что данные там не хранятся) в массив не помещаются, то интерпретатор внутри себя создает новый массив большего размера (обычно в два раза) и переносит все ссылки туда. Динамические массивы очень упрощают процесс разработки, но за это тоже приходится платить скоростью.</p>
15
<p>Кроме того, массивы в динамических языках тоже динамические. То есть их размер может увеличиваться или уменьшаться в процессе работы программы. Технически это работает так: если ссылки (помним, что данные там не хранятся) в массив не помещаются, то интерпретатор внутри себя создает новый массив большего размера (обычно в два раза) и переносит все ссылки туда. Динамические массивы очень упрощают процесс разработки, но за это тоже приходится платить скоростью.</p>