From ec805e7f085e2b971fec23fe90f982b8a9b3c927 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sun, 5 Feb 2023 19:45:16 +0300 Subject: [PATCH] =?UTF-8?q?4=20=D0=B3=D0=BB=D0=B0=D0=B2=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- {03-инструкция => 03-инструкции}/README.md | 0 .../src/chapter-3-11/app.d | 0 .../src/chapter-3-12/app.d | 0 .../src/chapter-3-13/app.d | 0 .../src/chapter-3-6/app.d | 0 .../src/chapter-3-7-4/app.d | 0 .../src/chapter-3-7-5/app.d | 0 .../src/chapter-3-9/app.d | 0 .../README.md | 1583 +++++++++++++++++ .../images/image-4-1-4-1.png | Bin 0 -> 12665 bytes .../images/image-4-1-4-2.png | Bin 0 -> 15501 bytes .../images/image-4-1-4-3.png | Bin 0 -> 15738 bytes .../images/image-4-1-9-1.png | Bin 0 -> 30530 bytes .../images/image-4-3-1.png | Bin 0 -> 11074 bytes README.md | 2 +- 15 files changed, 1584 insertions(+), 1 deletion(-) rename {03-инструкция => 03-инструкции}/README.md (100%) rename {03-инструкция => 03-инструкции}/src/chapter-3-11/app.d (100%) rename {03-инструкция => 03-инструкции}/src/chapter-3-12/app.d (100%) rename {03-инструкция => 03-инструкции}/src/chapter-3-13/app.d (100%) rename {03-инструкция => 03-инструкции}/src/chapter-3-6/app.d (100%) rename {03-инструкция => 03-инструкции}/src/chapter-3-7-4/app.d (100%) rename {03-инструкция => 03-инструкции}/src/chapter-3-7-5/app.d (100%) rename {03-инструкция => 03-инструкции}/src/chapter-3-9/app.d (100%) create mode 100644 04-массивы-ассоциативные-массивы-и-строки/images/image-4-1-4-1.png create mode 100644 04-массивы-ассоциативные-массивы-и-строки/images/image-4-1-4-2.png create mode 100644 04-массивы-ассоциативные-массивы-и-строки/images/image-4-1-4-3.png create mode 100644 04-массивы-ассоциативные-массивы-и-строки/images/image-4-1-9-1.png create mode 100644 04-массивы-ассоциативные-массивы-и-строки/images/image-4-3-1.png diff --git a/03-инструкция/README.md b/03-инструкции/README.md similarity index 100% rename from 03-инструкция/README.md rename to 03-инструкции/README.md diff --git a/03-инструкция/src/chapter-3-11/app.d b/03-инструкции/src/chapter-3-11/app.d similarity index 100% rename from 03-инструкция/src/chapter-3-11/app.d rename to 03-инструкции/src/chapter-3-11/app.d diff --git a/03-инструкция/src/chapter-3-12/app.d b/03-инструкции/src/chapter-3-12/app.d similarity index 100% rename from 03-инструкция/src/chapter-3-12/app.d rename to 03-инструкции/src/chapter-3-12/app.d diff --git a/03-инструкция/src/chapter-3-13/app.d b/03-инструкции/src/chapter-3-13/app.d similarity index 100% rename from 03-инструкция/src/chapter-3-13/app.d rename to 03-инструкции/src/chapter-3-13/app.d diff --git a/03-инструкция/src/chapter-3-6/app.d b/03-инструкции/src/chapter-3-6/app.d similarity index 100% rename from 03-инструкция/src/chapter-3-6/app.d rename to 03-инструкции/src/chapter-3-6/app.d diff --git a/03-инструкция/src/chapter-3-7-4/app.d b/03-инструкции/src/chapter-3-7-4/app.d similarity index 100% rename from 03-инструкция/src/chapter-3-7-4/app.d rename to 03-инструкции/src/chapter-3-7-4/app.d diff --git a/03-инструкция/src/chapter-3-7-5/app.d b/03-инструкции/src/chapter-3-7-5/app.d similarity index 100% rename from 03-инструкция/src/chapter-3-7-5/app.d rename to 03-инструкции/src/chapter-3-7-5/app.d diff --git a/03-инструкция/src/chapter-3-9/app.d b/03-инструкции/src/chapter-3-9/app.d similarity index 100% rename from 03-инструкция/src/chapter-3-9/app.d rename to 03-инструкции/src/chapter-3-9/app.d diff --git a/04-массивы-ассоциативные-массивы-и-строки/README.md b/04-массивы-ассоциативные-массивы-и-строки/README.md index e69de29..efcda15 100644 --- a/04-массивы-ассоциативные-массивы-и-строки/README.md +++ b/04-массивы-ассоциативные-массивы-и-строки/README.md @@ -0,0 +1,1583 @@ +# 4. Массивы, ассоциативные массивы и строки + +- [4.1. Динамические массивы](#4-1-динамические-массивы) + - [4.1.1. Длина](#4-1-1-длина) + - [4.1.2. Проверка границ](#4-1-2-проверка-границ) + - [4.1.3. Срезы](#4-1-3-срезы) + - [4.1.4. Копирование](#4-1-4-копирование) + - [4.1.5. Проверка на равенство](#4-1-5-проверка-на-равенство) + - [4.1.6. Конкатенация](#4-1-6-конкатенация) + - [4.1.7. Поэлементные операции](#4-1-7-поэлементные-операции) + - [4.1.8. Сужение](#4-1-8-сужение) + - [4.1.9. Расширение](#4-1-9-расширение) + - [4.1.10. Присваивание значения свойству .length](#4-1-10-присваивание-значения-свойству-length) +- [4.2. Массивы фиксированной длины](#4-2-массивы-фиксированной-длины) + - [4.2.1. Длина](#4-2-1-длина) + - [4.2.2. Проверка границ](#4-2-2-проверка-границ) + - [4.2.3. Получение срезов](#4-2-3-получение-срезов) + - [4.2.4. Копирование и неявные преобразования](#4-2-4-копирование-и-неявные-преобразования) + - [4.2.5. Проверка на равенство](#4-2-5-проверка-на-равенство) + - [4.2.6. Конкатенация](#4-2-6-конкатенация) + - [4.2.7. Поэлементные операции](#4-2-7-поэлементные-операции) +- [4.3. Многомерные массивы](#4-3-многомерные-массивы) +- [4.4. Ассоциативные массивы](#4-4-ассоциативные-массивы) + - [4.4.1. Длина](#4-4-1-длина) + - [4.4.2. Чтение и запись ячеек](#4-4-2-чтение-и-запись-ячеек) + - [4.4.3. Копирование](#4-4-3-копирование) + - [4.4.4. Проверка на равенство](#4-4-4-проверка-на-равенство) + - [4.4.5. Удаление элементов](#4-4-5-удаление-элементов) + - [4.4.6. Перебор элементов](#4-4-6-перебор-элементов) + - [4.4.7. Пользовательские типы](#4-4-7-пользовательские-типы) +- [4.5. Строки]() + - [4.5.1. Кодовые точки]() + - [4.5.2. Кодировки]() + - [4.5.3. Знаковые типы]() + - [4.5.4. Массивы знаков + бонусы = строки]() + - [4.5.4.1. Цикл foreach применительно к строкам]() +- [4.6. Опасный собрат массива – указатель]() +- [4.7. Справочник]() + +Предыдущие главы лишь косвенно знакомили нас с массивами, ассоциативными массивами и строками (тут выражение, там литерал), пора уже познакомиться с ними по-настоящему. Оперируя только этими тремя типами данных, можно написать много хорошего кода, так что теперь, когда в нашем арсенале уже есть выражения и инструкции, самое время побольше узнать о массивах, ассоциативных массивах и строках. + +[В начало ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +## 4.1. Динамические массивы + +Язык D предлагает очень простую, но гибкую абстракцию массивов. Для типа `T` справедливо, что `T[]` – это тип, представляющий собой непрерывную область памяти, содержащую элементы типа `T`. В терминах D `T[]` – это «массив значений типа `T`», или просто «массив значений `T`». + +Динамический массив создается с помощью выражения `new` (см. раздел 2.3.6.1): + +```d +int[] array = new int[20]; // Создать массив для 20 целых чисел +``` + +Более простой и удобный вариант: + +```d +auto array = new int[20]; // Создать массив для 20 целых чисел +``` + +Все элементы только что созданного массива типа `T[]` инициализируются значением `T.init` (для целых чисел это `0`). После того как массив создан, для доступа к его элементам служит индексирующее выражение `array[n]`: + +```d +auto array = new int[20]; +auto x = array[5]; // Корректны индексы от 0 до 19 +assert(x == 0); // Начальное значение для всех элементов массива: int.init = 0 +array[7] = 42; // Элементам массива можно присваивать значения +assert(array[7] == 42); +``` + +Число элементов, заданное в выражении `new`, не обязательно константа. Например, следующая программа создает массив случайной длины и заполняет его случайными числами, для генерации которых вызывает функцию `uniform` из модуля `std.random`: + +```d +import std.random; + +void main() +{ + // От 1 до 127 элементов + auto array = new double[uniform(1, 128)]; + foreach (i; 0 .. array.length) + { + array[i] = uniform(0.0, 1.0); + } + ... +} +``` + +Цикл `foreach` можно переписать, чтобы обращаться непосредственно к каждому элементу массива, не используя индексы (см. раздел 3.7.5): + +```d +foreach (ref element; array) +{ + element = uniform(0.0, 1.0); +} +``` + +Ключевое слово `ref` сообщает компилятору, что в нашем коде присваивания элементу `element` должны отражаться в исходном массиве. Иначе значения присваивались бы только копиям элементов массива. + +Можно инициализировать массив особыми значениями (отличными от значений по умолчанию) с помощью литерала массива: + +```d +auto somePrimes = [ 2, 3, 5, 7, 11, 13, 17 ]; +``` + +Еще один способ создать массив – дублировать существующий массив. При обращении к свойству `.dup` массива создается поэлементная копия этого массива: + +```d +auto array = new int[100]; +... +auto copy = array.dup; +assert(array !is copy); // Это разные массивы, +assert(array == copy); // но с одинаковым содержимым +``` + +Наконец, если вы просто определите переменную типа `T[]`, не инициализируя ее или инициализируя значением `null`, то получите «пустой массив» (null array). Пустой массив не имеет элементов, проверка на равенство такого массива константе `null` возвращает `true`. + +```d +string[] a; // То же, что string[] a = null +assert(a is null); +assert(a == null); // То же, что выше +a = new string[2]; +assert(a !is null); +a = a[0 .. 0]; +assert(a !is null); +``` + +Благодаря последней строке этого кода обнаруживается нечто странное: пустой массив – это необязательно `null`. + +[В начало ⮍](#4-1-динамические-массивы) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.1.1. Длина + +Динамические массивы всегда «помнят» свою длину. Доступ к этому значению предоставляет свойство `.length` массива: + +```d +auto array = new short[55]; +assert(array.length == 55); +``` + +Выражение `array.length` часто используется внутри индексирующего выражения для массива `array`. Например, обратиться к последнему элементу массива array можно с помощью выражения `array[array.length - 1]`. Чтобы упростить подобную запись, было разрешено внутри индексирующих выражений обозначать длину индексируемого массива идентификатором `$`. + +```d +auto array = new int[10]; +array[9] = 42; +assert(array[$ - 1] == 42); +``` + +Изменение длины массива обсуждается в разделах 4.1.8–4.1.10. + +[В начало ⮍](#4-1-1-длина) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.1.2. Проверка границ + +Что произойдет, если выполнить следующий код? + +```d +auto array = new int[10]; +auto invalid = array[100]; +``` + +Учитывая, что массивы всегда знают свою длину, можно легко вставить соответствующие проверки, так что вопрос о выполнимости задачи не стоит. Все дело в том, что проверка границ – это одна из тех вещей, которые ставят программиста перед мучительным выбором между быстродействием и безопасностью. + +По соображениям безопасности необходимо постоянно проверять каким-либо способом корректность обращений к массиву. Доступ к памяти за пределами массива может привести к неконтролируемому поведению программы, сделать ее уязвимой для эксплойтов, вызывать сбои. + +В то же время при текущей технологии компилирования полная проверка границ все еще сильно влияет на быстродействие. Эффективная проверка границ – тема для серьезного исследования. Популярный подход состоит в том, что сначала расставляют проверки везде, где выполняется обращение к массиву, а затем убирают те из них, которые статический анализатор сочтет излишними. Обычно этот процесс быстро усложняется, особенно в тех случаях, когда при использовании массивов пересекаются границы процедур и модулей. Применяемые сегодня методы проверки границ требуют длительного анализа даже для скромных программ и позволяют избавиться лишь от части ненужных проверок. + +В отношении головоломки с проверкой границ D находится между двух огней. Язык пытается одновременно предоставить как безопасность и удобство, свойственные современным языкам, так и предельное, ничем не ограниченное быстродействие, желательное для языка системного уровня. Проблема проверки границ подразумевает выбор между этими двумя крайностями, и D позволяет сделать этот выбор вам самим, вместо того чтобы сделать его за вас. + +Во время компиляции D дважды делает выбор: +- между безопасным и системным модулями (см. раздел 11.2.2); +- между промежуточной (non-release) и итоговой (release) сборками (см. раздел 10.6). + +D различает «безопасные» (safe) и «системные» (system) модули. Средний уровень безопасности – «доверенный» (trusted). Подразумеваются модули, которые предоставляют безопасный интерфейс, но могут осуществлять доступ системного уровня в рамках своей реализации. Выбор уровня доверенности написанных вами модулей – за вами. Во время компиляции безопасного модуля компилятор статически отключает все средства языка (включая непроверенную индексацию массивов), которые могут вызвать некорректный доступ к памяти. Компилируя системный или доверенный модуль, компилятор разрешает необработанный, непроверенный доступ к аппаратному обеспечению. Вы можете задать уровень определенной части модуля (безопасный, системный или доверенный), воспользовавшись специальной опцией командной строки или вставив атрибут: + +```d +@safe; +``` + +или + +```d +@trusted; +``` + +или + +```d +@system; +``` + +Выбранный уровень безопасности «действует» начиная с точки вставки соответствующего атрибута до следующей точки вставки или до конца файла (если больше нет вставленных атрибутов). + +Механизм безопасности модулей подробно описан в главе 11, а сейчас главное из всей этой информации то, что вы как разработчик можете выбрать для своего модуля атрибут `@safe`, `@trusted` или `@system`. + +Решение о выполнении *итоговой* сборки вашего приложения принимается независимо от безопасности модулей. Указать компилятору D собрать итоговую версию программы можно с помощью флага командной строки (`-release` в эталонной реализации). Для безопасного модуля границы проверяются *всегда*. Для системного модуля проверки границ вставляются только при *промежуточной* (не итоговой) сборке. При промежуточной сборке также вставляются и другие проверки, такие как выражения `assert` и проверки контрактов (последствия выбора итоговой сборки подробно обсуждаются в главе 10). Взаимосвязь между степенью безопасности модуля (безопасный/системный модуль) и режимом сборки (итоговая/промежуточная сборка) отражена в табл. 4.1. + +*Таблица 4.1. Проверка границ в зависимости от вида модуля и режима сборки* + +||Безопасный модуль|Системный модуль| +|-|:-:|:-:| +|Промежуточная сборка|✓|✓| +|Итоговая сборка (флаг `-release` для компилятора `dmd`)|✓|☠| + +Вас предупредили. + +[В начало ⮍](#4-1-2-проверка-границ) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.1.3. Срезы + +Срезы – это мощное средство, позволяющее выбирать и использовать непрерывный фрагмент массива. Например, можно напечатать только вторую половину массива: + +```d +import std.stdio; + +void main() +{ + auto array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; + // Напечатать только вторую половину + writeln(array[$ / 2 .. $]); +} +``` + +Эта программа напечатает: + +```sh +5 6 7 8 9 +``` + +Чтобы получить срез массива `array`, используйте форму записи `array[m .. n]` для выбора части массива, которая начинается элементом с индексом `m` и заканчивается элементом с индексом `n-1` (включая и этот элемент). Срез имеет тот же тип, что и сам массив, поэтому, например, можно присвоить срез тому же массиву, с которого сделан этот срез: + +```d +array = array[$ / 2 .. $]; +``` + +В выражениях, обозначающих начало и конец среза, может участвовать идентификатор `$`, как и в случае обычной индексации, обозначающий длину массива, срез которого требуется получить. Если `m` и `n` равны, это не является ошибкой: результатом в этом случае будет пустой срез. Нельзя задать `m > n` или `n > array.length`. Проверка таких «незаконных случаев» выполняется в соответствии с порядком, описанным в разделе 4.1.2. + +Выражение `array[0 .. $]` получает срез, включающий все содержимое массива `array`. Это выражение встречается довольно часто, и тут язык помогает программистам, позволяя вместо записи `array[0 .. $]` использовать краткую форму `array[]`. + +[В начало ⮍](#4-1-3-срезы) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.1.4. Копирование + +Объект массива содержит (или может почти мгновенно вычислить) как минимум два ключевых значения – верхнюю и нижнюю границы своих данных. Например, после выполнения кода + +```d +auto a = [1, 5, 2, 3, 6]; +``` + +объект `a` окажется в состоянии, показанном на рис. 4.1. Массив «видит» только область, заключенную между его границами; заштрихованная область ему недоступна. + +![image-4-1-4-1](images/image-4-1-4-1.png) + +***Рис. 4.1.*** *Объект массива ссылается на область памяти, содержащую пять элементов* + +(Возможны и другие формы внутреннего представления массива, например, хранение адреса первого элемента и размера занимаемой области памяти или адреса первого элемента и адреса элемента, следующего за последним элементом. Тем не менее все представления в итоге предоставляют доступ к одной и той же существенной информации.) + +Инициализация массива другим массивом (`auto b = a`), равно как и присваивание одного массива другому (`int[] b; … b = a;`) не влечет скрытого автоматического копирования данных. Как показано на рис. 4.2, эти действия просто заставляют `b` ссылаться на ту же область памяти, что и `a`. + +Более того, получение среза массива `b` сокращает область памяти, «видимую» `b`, также без всякого копирования `b`. При условии что исходное состояние массива задано на рис. 4.2, выполнение инструкции + +```d +b = b[1 .. $ - 2]; +``` + +![image-4-1-4-2](images/image-4-1-4-2.png) + +***Рис. 4.2.*** *При выполнении инструкции `auto b = a;` содержимое a не копируется: вместо этого создается объект типа «массив», который ссылается на те же данные* + +приведет лишь к сокращению диапазона, доступного `b`, без какого-либо копирования данных (рис. 4.3). + +![image-4-1-4-3](images/image-4-1-4-3.png) + +***Рис. 4.3.*** *Выполнение инструкции `b = b[1 .. $ - 2];` ведет к сужению области памяти, управляемой переменной b, при этом выбранный срез не копируется* + +Прямым следствием совместного использования (разделения) данных, проиллюстрированного рис. 4.2 и 4.3, является то, что изменение элемента одного массива может отразиться и на других массивах: + +```d +int[] array = [0, 1, 2]; +int[] subarray = array[1 .. $]; +assert(subarray.length == 2); +subarray[1] = 33; +assert(array[2] == 33); // Изменение массива subarray отразилось на массиве array +``` + +[В начало ⮍](#4-1-4-копирование) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.1.5. Проверка на равенство + +Выражение `a is b` (см. раздел 2.3.4.3) сравнивает границы двух массивов на равенство и возвращает `true`, если и только если `a` и `b` привязаны в точности к одной и той же области памяти. Никакая проверка содержимого массивов не производится. + +Для поэлементной проверки на равенство массивов `a` и `b` служит операция вида `a == b` или противоположная ей `a != b` (см. раздел 2.3.12). + +```d +auto a = ["hello", "world"]; +auto b = a; +assert(a is b); // Тест пройден, у a и b одни те же границы +assert(a == b); // Естественно, тест пройден +b = a.dup; +assert(a == b); // Тест пройден, a и b равны, хотя занимают разные области памяти +assert(a !is b); // Тест пройден, a и b различны, хотя имеют одинаковое содержимое +``` + +При поэлементном сравнении массивов просматриваются все элементы обоих массивов и соответствующие пары сравниваются по очереди с помощью оператора `==`. + +[В начало ⮍](#4-1-5-проверка-на-равенство) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.1.6. Конкатенация + +Конструкция + +```d +содержимое1 ~ содержимое2 +``` + +представляет собой выражение конкатенации. Результатом конкатенации является новый массив, содержимое которого представляет собой `содержимое1`, за которым следует `содержимое2`. Операндами в выражении конкатенации могут быть: два массива (типы `T[]` и `T[]`), массив и значение (типы `T[]` и `T`), значение и массив (типы `T` и `T[]`). + +```d +int[] a = [0, 10, 20]; +int[] b = a ~ 42; +assert(b == [0, 10, 20, 42]); +a = b ~ a ~ 15; +assert(a.length == 8); +``` + +Под результирующий массив всегда выделяется новая область памяти. + +[В начало ⮍](#4-1-6-конкатенация) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.1.7. Поэлементные операции + +Некоторые операции применяются к массиву в целом, без явного указания на элементы массива. Чтобы применить поэлементную операцию, в выражении рядом с каждым срезом (в том числе слева от оператора присваивания) укажите `[]` или `[m .. n]`, как здесь: + +```d +auto a = [ 0.5, -0.5, 1.5, 2 ]; +auto b = [ 3.5, 5.5, 4.5, -1 ]; +auto c = new double[4]; // Память под массив должна быть уже выделена +c[] = (a[] + b[]) / 2; // Рассчитать среднее арифметическое a и b +assert(c == [ 2.0, 2.5, 3.0, 0.5 ]); +``` + +В поэлементной операции могут участвовать: +- простое значение, например `5`; +- срез, явно указанный с помощью `[]` или `[m .. n]`, например `a[]` или `a[1 .. $ - 1]`; +- любое корректное выражение на D с участием сущностей, определенных в двух предыдущих пунктах, унарных операторов `-` и `~`, а также бинарных операторов `+`, `-`, `*`, `/`, `%`, `^^`, `^`, `&`, `|`, `=`, `+=`, `-=`, `*=`, `/=`, `%=`, `^=`, `&=` и `|=`. + +Поэлементная операция равносильна циклу, в котором поочередно каждому элементу массива, указанного слева от оператора присваивания, присваивается результат операции над элементами массивов с тем же индексом, расположенной справа от оператора присваивания. Например, присваивание + +```d +auto a = [1.0, 2.5, 3.6]; +auto b = [4.5, 5.5, 1.4]; +auto c = new double[3]; +c[] += 4 * a[] + b[]; +``` + +равносильно циклу + +```d +foreach (i; 0 .. c.length) +{ + c[i] += 4 * a[i] + b[i]; +} +``` + +Проверка границ выполняется в соответствии с порядком, описанным в разделе 4.1.2. + +Используя явно заданные срезы (заканчивающиеся парой скобок `[]` или обозначением диапазона `[m .. n]`), числа и допустимые операторы, с помощью круглых скобок можно создавать выражения любой глубины и сложности, например: + +```d +double[] a, b, c; +double d; +... +a[] = -(b[] * (c[] + 4)) + c[] * d; +``` + +Из поэлементных операций чаще всего применяются простое заполнение ячеек (элементов) массива содержимым и их копирование: + +```d +int[] a = new int[128]; +int[] b = new int[128]; +... +b[] = -1; // Заполнить все ячейки b значением -1 +a[] = b[]; // Скопировать все данные из b в a +``` + +**Предупреждение** + +Поэлементные операции очень мощны, а чем больше мощность, тем больше ответственность. Именно вы отвечаете за отсутствие перекрывания между l- и r-значениями каждого присваивания в поэлементной операции. Приводя высокоуровневые операции к примитивным операциям над векторами, которые может выполнять конечный процессор (на котором будет исполняться программа), компилятор вправе считать, что это именно так. Если вы намеренно используете перекрывание, то напишите циклы обработки элементов массива вручную, чтобы компилятор не смог выполнить какие-то непроверенные присваивания. + +[В начало ⮍](#4-1-7-поэлементные-операции) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.1.8. Сужение + +Сужение массива означает, что массив должен «забыть» о некотором количестве своих начальных или конечных элементов без перемещения остальных. Ограничение на перемещение очень важно; если бы оно не было обязательным, массивы было бы легко сужать, просто создавая новую копию с теми элементами, которые требуется оставить. + +Тем не менее сузить массив очень просто: нужно просто присвоить массиву срез его самого: + +```d +auto array = [0, 2, 4, 6, 8, 10]; +array = array[0 .. $ - 2]; // Сужение справа на два элемента +assert(array == [0, 2, 4, 6]); +array = array[1 .. $]; // Сужение слева на один элемент +assert(array == [2, 4, 6]); +array = array[1 .. $ - 1]; // Сужение с обеих сторон +assert(array == [4]); +``` + +Все операции сужения выполняются за одно и то же время, которое не зависит от длины массива (практически они состоят из пары присваиваний с операндами-словами). Технически простое сужение массива с обоих концов – очень полезное средство языка D. (Другие языки позволяют легко сужать массивы справа, но не слева, поскольку такая операция повлекла бы перемещение всех элементов массива, чтобы сохранить положение левой границы массива.) В языке D вы можете получить копию массива и последовательно сужать ее, постоянно обрабатывая элементы в начале и в конце массива, в полной уверенности, что операции сужения, выполняемые за фиксированное время, не нанесут ощутимого ущерба быстродействию программы. + +Напишем для примера маленькую программу, определяющую, является ли массив, переданный в командной строке, палиндромом. Массив-палиндром симметричен относительно своей середины, то есть `[5, 17, 8, 17, 5]` – это палиндром, а `[5, 7, 8, 7]` – нет. Для решения этой задачи нам потребуются несколько помощников. Сначала нужно извлечь аргументы командной строки в массив значений типа `string`. Эту задачу любезно возьмет на себя функция `main`, если определить ее как `main(string[] args)`. Затем нужно преобразовать эти значения типа `string` в значения типа `int`, для чего мы воспользуемся функцией с говорящим именем `to` из модуля `std.conv`. Результат вычисления выражения `to!int(str)` – распознанное в строке `str` значение типа `int`. Все это помогает нам написать программу, которая проверяет, является ли введенный массив палиндромом: + +```d +import std.conv, std.stdio; + +int main(string[] args) +{ + // Избавиться от имени программы + args = args[1 .. $]; + while (args.length >= 2) + { + if (to!int(args[0]) != to!int(args[$ - 1])) + { + writeln("не палиндром"); + return 1; + } + args = args[1 .. $ - 1]; + } + writeln("палиндром"); + return 0; +} +``` + +Сначала программе нужно удалить свое имя из списка аргументов, формат которого соответствует традициям языка C. Если вызвать нашу программу (назовем ее `palindrome`) следующим образом: + +```sh +palindrome 34 95 548 +``` + +то содержимое массива `args` примет вид `["palindrome", "34", "95", "548"]`. Вот где пригодилось сужение слева `args = args[1 .. $]`: оно сокращает массив args до массива `["34", "95", "548"]`. Затем программа пошагово сравнивает элементы на концах массива. Если они не равны, то дальше можно не сравнивать: пишем `"не палиндром"` и закругляемся. А если проверка прошла успешно, то сужаем массив `args` с обоих концов. Только если все проверки возвратят `true`, а в массиве `args` останется не больше одного элемента (пустые массивы и массивы из одного элемента программа считает палиндромами), программа напечатает `"палиндром"` и завершится. Несмотря на то что программа активно манипулирует массивами, после инициализации массива `args` память не перераспределялась ни разу. Работа начинается c обращения к массиву `args` (память под который была выделена заранее), а потом он только сужается. + +[В начало ⮍](#4-1-8-сужение) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.1.9. Расширение + +Перейдем к расширению массивов. Расширить массив позволяет оператор присоединения `~=`, например: + +```d +auto a = [87, 40, 10]; +a ~= 42; +assert(a == [87, 40, 10, 42]); +a ~= [5, 17]; +assert(a == [87, 40, 10, 42, 5, 17]); +``` + +У расширения массивов есть пара тонких моментов, связанных с перераспределением памяти. Рассмотрим код: + +```d +auto a = [87, 40, 10, 2]; +auto b = a; // Теперь a и b ссылаются на одну и ту же область памяти +a ~= [5, 17]; // Присоединить к a +a[0] = 15; // Изменить a[0] +assert(b[0] == 15); // Будет ли пройден тест? +``` + +Повлияет ли выполненное после присоединения присваивание элементу `a[0`] на `b[0]`? Другими словами, будут ли `a` и `b` разделять данные после перераспределения памяти? Коротко на этот вопрос можно ответить так: `b[0]` может содержать 15, а может и не содержать – язык не дает никаких гарантий. + +Реальность такова, что в конце массива `a` не всегда достаточно места, чтобы перераспределить память под измененный массив в том же месте. Иногда перенос массива в другую область памяти бывает неизбежен. Проще всего добиться корректного поведения программы в подобных случаях, *всегда* перераспределяя память под массив `a` после присоединения к нему новых элементов с помощью оператора `~=`, то есть делая операцию `a ~= b` тождественной операции `a = a ~ b`, что означает: «Определить в новой области памяти массив, содержащий последовательность элементов массива `a`, к которой присоединена последовательность элементов массива `b`, и связать переменную `a` с полученным новым массивом». Такое поведение проще всего реализуется, но наносит серьезный урон быстродействию. Приведем пример. Обычно содержимое массивов пошагово наращивают в цикле: + +```d +int[] a; +foreach (i; 0 .. 100) +{ + a ~= i; +} +``` + +При 100 элементах приемлема любая стратегия расширения и неважно, какой вариант будет выбран, но с ростом массивов только жесткие решения останутся относительно быстрыми. Не очень привлекательный подход состоит в том, чтобы разрешить удобный, но неэффективный синтаксис расширения `a ~= b` и применять его только с короткими массивами, а для длинных массивов использовать другой, менее удобный синтаксис. Маловероятно, что самый простой и интуитивно понятный синтаксис сработает как с короткими, так и с длинными массивами. + +D оставляет оператору `~=` свободу перераспределять память по своему усмотрению: он может выполнять расширение с переносом массива в новую область памяти, но старается оставить его на «старом месте», если после массива достаточно свободной памяти для размещения новых элементов. Выбор в пользу той или иной альтернативы определяется исключительно реализацией `~=`, но с той гарантией, что программа, выполняющая много присоединений к одному и тому же массиву, будет обладать хорошим *средним* быстродействием. + +На рис. 4.4 показаны два возможных исхода расширения `a ~= [5, 17]`. + +![image-4-1-9-1](images/image-4-1-9-1.png) + +***Рис. 4.4.*** *Два возможных исхода попытки расширить массив `a`* + +В зависимости от того, как работает низкоуровневый менеджер памяти, массив может расширяться разными способами: +- Обычно менеджеры памяти выделяют память только фиксированными блоками (то есть блоками размера, кратного 2). Поэтому возможно, что при запросе 700 байт будет выделено 1024 байта памяти, из которых 324 будут пустовать. Получив запрос на расширение, массив может проверить, нет ли такой незанятой памяти, и использовать ее. +- Если собственной незанятой памяти не осталось, массив может затеять более сложные переговоры с низкоуровневым менеджером памяти: «Послушай, мне бы немного памяти на благое дело. Нет ли случайно рядом со мной свободного блока?» Если менеджер памяти обнаружит незанятый блок справа от текущего блока массива, то объединит их. Такая операция называется *слиянием* (*coalescing*). После этого расширение может продолжаться без перемещения каких-либо данных. +- Наконец, если справа от текущего блока совсем нет места, менеджер памяти выделяет новый блок памяти, и все содержимое массива копируется туда. Реализация менеджера памяти может принудительно резервировать дополнительную область памяти, например, обнаружив повторяющиеся расширения одного и того же массива. + +Расширяющийся массив никогда не «наступит» на существующий массив. Например: + +```d +int[] a = [0, 10, 20, 30, 40, 50, 60, 70]; +auto b = a[4 .. $]; +a = a[0 .. 4]; +// Сейчас a и b примыкают друг к другу +a ~= [0, 0, 0, 0]; +assert(b == [40, 50, 60, 70]); // Тест пройден; массив a был перенесен в новую область памяти +``` + +Этот код искусно заставляет массив `a` думать, что в конце области памяти, которую он занимает, есть свободное место: первоначально массив `a` был больше по размеру, затем массив `b` занял вторую половину массива `a`, а сам массив `a` сузился до своей первой половины. Перед добавлением новых элементов в массив `a` массивы `a` и `b` занимали соседние области памяти: массив `a` находился слева от массива `b`. Однако успешное выполнение теста `assert` после добавления новых элементов в массив a подтвердило, что этот массив был перенесен в другую область памяти, а не расширился на том же месте. Оператор расширения добавляет в массив элементы без изменения адреса массива, только если уверен, что справа от расширяющегося массива нет другого массива, и при малейшем сомнении всегда готов подстраховаться, перераспределив память. + +[В начало ⮍](#4-1-9-расширение) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.1.10. Присваивание значения свойству .length + +Присвоив значение свойству `.length` массива, вы можете сузить или расширить его, в зависимости от отношения новой длины к старой. Например: + +```d +int[] array; +assert(array.length == 0); +array.length = 1000; // Расширяется +assert(array.length == 1000); +array.length = 500; // Сужается +assert(array.length == 500); +``` + +Если массив расширяется в результате присваивания свойству `.length`, добавленные элементы инициализируются значением `T.init`. Стратегия расширения и гарантии идентичности в этом случае аналогичны добавлению элементов с помощью оператора `~=` (см. раздел 4.1.9). + +Если массив сжимается в результате присваивания свойству `.length`, D гарантирует, что массив не будет перемещен. Практически, если `n <= a.length`, `a.length = n` эквивалентно `a = a[0 .. n]`. (Однако нет гарантии, что массив не будет перемещен в результате последующих расширений.) + +Можно одновременно выполнить чтение, изменение и запись значения свойства `.length` следующим способом: + +```d +auto array = new int[10]; +array.length += 1000; // Расширяется +assert(array.length == 1010); +array.length /= 10; // Сужается +assert(array.length == 101); +``` + +Здесь нет никакой магии; все, что необходимо сделать компилятору, – это переписать выражение `array.length ‹о›= b` в несколько иной форме: `array.length = array.length ‹о› b`. И все-таки немного магии тут есть (на самом деле, всего лишь ловкость рук): в переписанном выражении массив вычисляется всего лишь раз, что очень кстати, если реально `array` – это какое-то замысловатое выражение. + +[В начало ⮍](#4-1-10-присваивание-значения-свойству-length) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +## 4.2. Массивы фиксированной длины + +D также позволяет создать массив, длина которого известна во время компиляции. Пример объявления такого массива: + +```d +int[128] someInts; +``` + +Каждое сочетание типа `T` и размера `n` представляет собой уникальный тип `T[n]`: например, тип `uint[10]` отличается от типа `uint[11]`, равно как и от типа `int[10]`. + +Память под все массивы фиксированной длины выделяется статически, в месте их объявления. Если значение массива определено глобально, память под него выделяется в сегменте данных программы, индивидуальном для каждого потока. Если же массив определяется внутри функции, он будет размещен в стеке этой функции при ее вызове. (Это означает, что определять слишком большие массивы внутри функций довольно опасно.) Однако если задать массив внутри функции с ключевым словом `static`, он займет блок памяти в сегменте данных потока, так что в этом случае риска переполнения стека нет. + +При создании массива фиксированной длины типа `T[n]` все его элементы инициализируются значением `T.init`. Например: + +```d +int[3] a; +assert(a == [0, 0, 0]); +``` + +Также можно инициализировать массив типа `T[n]` с помощью литерала: + +```d +int[3] a = [1, 2, 3]; +assert(a == [1, 2, 3]); +``` + +Но будьте осторожны: если в объявлении типа заменить `int[3]` ключевым словом `auto`, то по принятым в D правилам определения типов массиву `a` будет присвоен тип `int[]`, а не `int[3]`. Несмотря на то что кажется логичным выбрать тип `int[3]`, в некотором смысле более «точный», чем `int[]`, на практике динамические массивы используются гораздо чаще массивов фиксированной длины, поэтому трактовка литералов массивов как массивов фиксированной длины отрицательно сказалась бы на удобстве языка, став источником многих неприятных сюрпризов. Кроме того, такое толкование литералов свело бы на нет смысл использования ключевого слова `auto` с массивами. Поэтому значениям, задаваемым литералом, `T[]` присваивается по умолчанию, а `T[n]` – если вы *просите* присвоить этот конкретный тип и при этом `n` соответствует числу значений в литерале (как в коде выше). + +Если вы инициализируете массив фиксированной длины типа `T[n]` с помощью единственного значения типа `T`, все ячейки массива будут заполнены этим значением. + +```d +int[4] a = -1; +assert(a == [-1, -1, -1, -1]); +``` + +Если вы планируете оставить массив неинициализированным и заполнить его во время исполнения программы, просто укажите в качестве инициализирующего значения ключевое слово `void`: + +```d +int[1024] a = void; +``` + +Возможность выделять память под массив, не инициализируя ее, особенно полезна, когда требуется задать большой массив под временный буфер. Будьте осторожны: неинициализированное целое число, скорее всего, никому особо не навредит, а вот неинициализированные значения ссылочных типов (таких как многомерные массивы) небезопасны. Доступ к элементам массива фиксированной длины осуществляется по индексу `a[i]`, как и к элементам динамических массивов. Просмотр массива фиксированной длины также практически идентичен просмотру динамического массива. Например, так создается массив, содержащий 1024 случайных числа: + +```d +import std.random; + +void main() +{ + double[1024] array; + foreach (i; 0 .. array.length) + { + array[i] = uniform(0.0, 1.0); + } + ... +} +``` + +В цикле можно не использовать индекс, осуществляя доступ к элементу массива по ссылке: + +```d +foreach (ref element; array) +{ + element = uniform(0.0, 1.0); +} +``` + +[В начало ⮍](#4-2-массивы-фиксированной-длины) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.2.1. Длина + +Очевидно, что массив фиксированной длины знает свой размер, потому что он «прошит» в его типе. В отличие от длины динамических массивов, свойство `.length` массива фиксированной длины неизменяемо и является статической константой. Это означает, что вы можете использовать это свойство везде, где требуется значение, известное во время компиляции, например, в качестве размера другого массива фиксированной длины при его определении[^1]: + +```d +int[100] quadrupeds; +int[4 * quadrupeds.length] legs; // Все в порядке, 400 ног +``` + +В индексирующем выражении массива `а` вместо записи `a.length` можно использовать идентификатор `$`, и значение этого выражения также будет известно во время компиляции. + +[В начало ⮍](#4-2-1-длина) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.2.2. Проверка границ + +Проверка границ массивов фиксированной длины имеет интересную особенность. Если индексирование осуществляется с помощью выражения, вычисляемого во время компиляции, компилятор всегда проверяет, корректно ли оно, и отказывается компилировать программу, если обнаруживает попытку доступа к памяти за пределами массива. Например: + +```d +int[10] array; +array[15] = 5; // Ошибка! Индекс 15 находится за пределами a[0 .. 10]! +``` + +Однако если индексирующее выражение вычисляется в процессе исполнения программы, проверка границ во время компиляции осуществляется настолько, насколько это возможно, а проверка границ во время исполнения программы делается по тем же правилам, что и проверка границ динамических массивов (см. раздел 4.1.2). + +[В начало ⮍](#4-2-2-проверка-границ) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.2.3. Получение срезов + +Получение среза массива типа `T[n]` порождает массив типа `T[]` без копирования данных: + +```d +int[5] array = [40, 30, 20, 10, 0]; +auto slice1 = array[2 .. $]; // slice1 имеет тип int[] +assert(slice1 == [20, 10, 0]); +auto slice2 = array[]; // Такой же, как array[0 .. $] +assert(slice2 == array); +``` + +Проверка границ во время компиляции выполняется для одной из границ массива или для обеих границ, если они известны на этом этапе. + +Если вы примените к массиву типа `T[n]` оператор среза, указав в качестве границ среза числа `a1` и `a2`, известные во время компиляции, и при этом *укажете*, что должен быть возвращен массив типа `T[a2 - a1]`, компилятор удовлетворит ваш запрос. (По умолчанию, то есть при наличии ключевого слова `auto`, возвращается тип среза `T[]`.) Например: + +```d +int[10] a; +int[] b = a[1 .. 7]; // Все в порядке +auto c = a[1 .. 7]; // Все в порядке, c также имеет тип int[] +int[6] d = a[1 .. 7]; // Все в порядке, срез a[1 .. 7] скопирован в d +``` + +[В начало ⮍](#4-2-3-получение-срезов) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.2.4. Копирование и неявные преобразования + +В отличие от динамических массивов, массивы фиксированной длины копируются по значению. Это означает, что при копировании массива, передаче его внутрь функции в качестве аргументов и возврате из функции копируется весь массив. Например: + +```d +int[3] a = [1, 2, 3]; +int[3] b = a; +a[1] = 42; +assert(b[1] == 2); // b – независимая копия a +int[3] fun(int[3] x, int[3] y) +{ + // x и y – копии переданных аргументов + x[0] = y[0] = 100; + return x; +} +auto c = fun(a, b); // c имеет тип int[3] +assert(c == [100, 42, 3]); +assert(b == [1, 2, 3]); // Вызов fun никак на отразился на b +``` + +Передача целых массивов по значению может быть неэффективной в случае большого массива, но у такого способа много преимуществ. Одно из них в том, что короткие массивы и передача по значению часто используются в высокопроизводительных вычислениях. Другое – в том, что от передачи по значению есть простое средство: когда бы вы ни пожелали передать массив по ссылке, просто используйте ключевое слово `ref` или автоматическое приведение к типу `T[]` (см. следующий абзац). Наконец, передача по значению делает работу с массивами фиксированной длины более согласованной с другими аспектами языка. (Раньше в D массивы фиксированной длины копировались по ссылке, но при такой семантике копирования многие случаи требуют особой обработки, что нарушает логику пользовательского кода.) + +Массив типа `T[n]` может быть неявно преобразован к типу `T[]`. Память под динамический массив, полученный таким способом, не выделяется заново: он просто привязывается к границам исходного массива. Поэтому преобразование считается небезопасным, если исходный массив расположен в стеке. Неявное преобразование типов облегчает передачу массивов фиксированной длины типа `T[n]` в функции, ожидающие значение типа `T[]`. Тем не менее, если функция возвращает значение типа `T[n]`, результат ее вызова не может быть автоматически преобразован к типу `T[]`. + +```d +double[3] point = [0, 0, 0]; +double[] test = point; // Все в порядке +double[3] fun(double[] x) +{ + double[3] result; + result[] = 2 * x[]; // Операция над массивом в целом + return result; +} +auto r = fun(point); // Все в порядке, теперь r имеет тип double[3] +``` + +Свойство `.dup` позволяет получить дубликат массива фиксированной длины (см. раздел 4.1), но вы получите не объект типа `T[n]`, а динамически выделенный массив типа `T[]`, содержащий копию массива фиксированной длины. Такое поведение оправданно, ведь чтобы получить копию статического массива `а` того же типа, не нужно прибегать ни к каким дополнительным ухищрениям – просто напишите `auto copy = a`. + +[В начало ⮍](#4-2-4-копирование-и-неявные-преобразования) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.2.5. Проверка на равенство + +Массивы фиксированной длины можно проверять на равенство с помощью операторов `is` и `==`, как и динамические массивы (см. раздел 4.1.5). Также можно смело использовать в проверках одновременно массивы обоих видов: + +```d +int[4] fixed = [1, 2, 3, 4]; +auto anotherFixed = fixed; +assert(anotherFixed !is fixed); // Не то же самое (копирование по значению) +assert(anotherFixed == fixed); // Те же данные +auto dynamic = fixed[]; // Получает границы массива fixed +assert(dynamic is fixed); +assert(dynamic == fixed); // Естественно +dynamic = dynamic.dup; // Создает копию +assert(dynamic !is fixed); +assert(dynamic == fixed); +``` + +[В начало ⮍](#4-2-5-проверка-на-равенство) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.2.6. Конкатенация + +Конкатенация выполняется по тем же правилам, что и для динамических массивов (см. раздел 4.1.6). Нужно лишь помнить важную деталь. Вы получите массив фиксированной длины, только если *явно запросите* массив фиксированной длины. Иначе вы получите заново выделен +ный динамический массив. Например: + +```d +double[2] a; +double[] b = a ~ 0.5; // Присоединить к double[2] значение, получить double[] +auto c = a ~ 0.5; // То же самое +double[3] d = a ~ 1.5; // Все в порядке, явный запрос массива фиксированной длины +double[5] e = a ~ d; // Все в порядке, явный запрос массива фиксированной длины +``` + +Если в качестве результата конкатенации `~` явно указан массив фиксированной длины, никогда не происходит динамического выделения памяти: статически выделяется блок памяти, и результат конкатенации копируется в него. + +[В начало ⮍](#4-2-6-конкатенация) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.2.7. Поэлементные операции + +Поэлементные операции с массивами фиксированной длины работают так же, как и одноименные операции для динамических массивов (см. раздел 4.1.7). Компилятор всегда старается проверить корректность доступа к элементам массивов фиксированной длины в поэлементных выражениях. + +[В начало ⮍](#4-2-7-поэлементные-операции) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +## 4.3. Многомерные массивы + +Поскольку запись `T[]` означает динамический массив элементов типа `T`, а `T[]`, в свою очередь, – тоже тип, легко сделать вывод, что `T[][]` – это массив элементов типа `T[]`, то есть массив массивов элементов типа `T`. Каждый элемент «внешнего» массива – это, в свою очередь, тоже массив, предоставляющий обычную функциональность, присущую массивам. Рассмотрим `T[][]` на практике: + +```d +auto array = new double[][5]; // Массив из пяти массивов, содержащих элементы типа double, первоначально каждый из них – null +// Сделать треугольную матрицу +foreach (i, ref e; array) +{ + e = new double[array.length - i]; +} +``` + +Здесь определен массив треугольной формы: первая строка содержит пять элементов типа `double`, вторая – четыре и так далее до последней строки (с номером 4), в которой всего один элемент. Многомерный массив, полученный простым составлением из динамических массивов, называют *зубчатым массивом* (*jagged array*), поскольку его строки могут иметь разную длину (в отличие от массива с ровным правым краем, содержащего строки одинаковой длины). На рис. 4.5 показано расположение массива `array` в памяти. + +Чтобы получить доступ к элементу зубчатого массива, поочередно укажите индексы для каждого измерения, например `array[3][1]` – обращение ко второму элементу четвертой строки зубчатого массива. + +![image-4-3-1](images/image-4-3-1.png) + +***Рис. 4.5.*** *Зубчатый массив из примера, содержащий треугольную матрицу* + +Зубчатые массивы не являются непрерывными. Плюс этого свойства в том, что такой массив может быть рассредоточен по разным областям памяти и не требует слишком большого непрерывного блока. Кроме того, возможность хранить строки разной длины позволяет хорошо экономить память. Минусом же является то, что «высокий и худой» массив с большим числом строк и малым числом столбцов требует больших накладных расходов, поскольку для хранения содержимого каждого столбца требуется массив. Например, массив из 1 000 000 строк, в каждом из которых всего по 10 значений типа `int`, занимает 2 000 000 слов (одна строка – один массив) плюс дополнительные расходы на неиспользованную память при выделении 1 000 000 маленьких блоков, что, в зависимости от реализации менеджера памяти, может оказаться ощутимо гораздо больше затрат на хранение содержимого каждой строки (на каждые 10 целых чисел нужно всего по 40 байт). + +При работе с зубчатыми массивами могут возникнуть проблемы со скоростью доступа и дружелюбностью кэша. Каждое обращение к элементам такого массива – это на самом деле два косвенных обращения: на первом шаге через внешний массив осуществляется доступ к нужной строке, а на втором – через внутренний массив – к столбцу. Построчный просмотр зубчатого массива – не проблема, если сначала получить строку, а потом ее использовать. Однако просмотр по столбцам – неиссякаемый источник кэш-промахов. + +Если число столбцов известно во время компиляции, можно легко совместить массив фиксированной длины с динамическим массивом: + +```d +enum size_t columns = 128; +// Определить матрицу c 64 строками и 128 столбцами +auto matrix = new double[columns][64]; +// Не нужно выделять память под каждую строку – они и так уже существуют +foreach (ref row; matrix) +{ + ... // Использовать строку типа double[columns] +} +``` + +В цикле из этого примера нужно обязательно использовать ключевое слово `ref`. Без него из-за передачи массива `double[columns]` по значению (см. раздел 4.2.4) создавалась бы копия каждой просматриваемой строки, что, скорее всего, отразилось бы на скорости выполнения кода. + +Если во время компиляции известно число и строк, и столбцов многомерного массива, то можно использовать массив фиксированной длины массивов фиксированной длины, как в примере: + +```d +enum size_t rows = 64, columns = 128; +// Выделить память под матрицу с 64 строками и 128 столбцами +double[columns][rows] matrix; +// Вообще не нужно выделять память под массив – это значение +foreach (ref row; matrix) +{ + ... // Использовать строку типа double[columns] +} +``` + +Чтобы получить доступ к элементу в строке `i` и столбце `j`, напишите `matrix[i][j]`[^2]. Немного странно, что в объявлении типа массива размеры измерений указаны «справа налево» (то есть `double[столбцы][строки]`), а при обращении к элементам массива индексы указываются «слева направо». Это объясняется тем, что `[]` и `[n]` в типах привязываются справа налево, а в выражениях – слева направо. + +Сочетая массивы фиксированной длины с динамическими массивами, можно получать разнообразные многомерные массивы. Например, `int[5][][15]` – это трехмерный массив из 15 динамически размещаемых массивов, состоящих из блоков по 5 элементов типа `int`. + +[В начало ⮍](#4-3-многомерные-массивы) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +## 4.4. Ассоциативные массивы + +Можно было бы представить массив как функцию, отображающую положительные целые числа (индексы) на значения некоторого произвольного типа (содержимое массива). Функция определена только для целых чисел на промежутке `[0; длина_массива - 1]` и задана в виде таблицы значений (собственно содержимого массива). + +С этой точки зрения ассоциативные массивы – некая обобщенная форма массивов. В качестве области определения ассоциативных массивов можно использовать (почти) любой тип. Каждому значению из области определения можно поставить в соответствие значение другого типа – точно так же, как это делается с ячейками массивов. Метод записи, применяемый в случае ассоциативных массивов, и другие связанные с ними алгоритмы отличаются от метода и алгоритмов для других массивов, но, как и обычный массив, ассоциативный массив предоставляет возможность быстро сохранить и выбрать значение по ключу. + +Как и ожидалось, тип ассоциативного массива задается как `V[K]`, где `K` – тип ключей, а `V` – тип ассоциированных с ними значений. Например, создадим и инициализируем ассоциативный массив, который отображает строки на целые числа: + +```d +int[string] aa = [ "здравствуй":42, "мир":75 ]; +``` + +Литерал ассоциативного массива (см. раздел 2.2.6) – это список разделенных запятыми пар вида `ключ : значение`, заключенный в квадратные скобки. В нашем примере литерал достаточно информативен, так что можно не указывать тип переменной `aa` явно, а просто написать: + +```d +auto aa = [ "здравствуй":42, "мир":75 ]; +``` + +[В начало ⮍](#4-4-ассоциативные-массивы) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.4.1. Длина + +Для любого ассоциативного массива aa свойство `aa.length` типа `size_t` возвращает число ключей в `aa` (а значит, и число значений, учитывая, что между ключами и значениями отношение один-к-одному). + +Ассоциативный массив, созданный по умолчанию (без литерала), имеет нулевую длину, а проверка на равенство такого массива константе `null` возвращает `true`. + +```d +string[int] aa; +assert(aa == null); +assert(aa.length == 0); +aa = [0:"zero", 1:"not zero"]; +assert(aa.length == 2); +``` + +В отличие от одноименного свойства массивов, свойство `.length` ассоциативных массивов предназначено только для чтения. Тем не менее можно очистить ассоциативный массив, присвоив его переменной значение `null`. + +[В начало ⮍](#4-4-1-длина) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.4.2. Чтение и запись ячеек + +Чтобы записать в ассоциативный массив `aa` новую пару ключ–значение, или заменить значение, уже поставленное в соответствие этому ключу, просто присвойте новое значение выражению `aa[key]`[^3], как здесь: + +```d +// Создать ассоциативный массив с соответствием строка/строка +auto aa = [ "здравствуй":"salve", "мир":"mundi" ]; +// Перезаписать значения +aa["здравствуй"] = "ciao"; +aa["мир"] = "mondo"; +// Создать несколько новых пар ключ–значение +aa["капуста"] = "cavolo"; +aa["моцарелла"] = "mozzarella"; +``` + +Чтобы прочитать из ассоциативного массива значение по ключу, просто воспользуйтесь выражением `aa[key]`. (Компилятор различает чтение и запись и вызывает для этого функции, которые немного отличаются друг от друга.) Продолжим предыдущий пример: + +```d +assert(aa["здравствуй"] == "ciao"); +``` + +Если вы попытаетесь прочитать значение по ключу, которого нет в ассоциативном массиве, возникнет исключительная ситуация. Но обычно генерация исключения в случае, когда ключ не обнаружен, – слишком строгая мера, чтобы быть полезной, поэтому для чтения ассоциативных массивов предоставляется альтернативная функция, возвращающая значение по умолчанию, если ключ не найден в массиве. Она реализована в виде метода `get`, принимающего два аргумента. Если при вызове `aa.get(ключ, значeние_по_умолчанию)` в массиве найден `ключ`, то функция возвращает соответствующее ему значение, а выражение `значение_по__умолчанию` не вычисляется; иначе `значение_по__умолчанию` вычисляется и метод возвращает результат этого вычисления. + +```d +assert(aa["здравствуй"] == "ciao"); +// Ключ "здравствуй" существует, поэтому второй аргумент игнорируется +assert(aa.get("здравствуй", "salute") == "ciao"); +// Ключ "здорово" не существует, возвратить второй аргумент +assert(aa.get("здорово", "buongiorno") == "buongiorno"); +``` + +Если вы просто хотите проверить, существует ли определенный ключ в ассоциативном массиве, воспользуйтесь оператором `in`[^4]: + +```d +assert("здравствуй" in aa); +assert("эй" !in aa); +// Попытка прочесть aa["эй"] вызвала бы исключение +``` + +[В начало ⮍](#4-4-2-чтение-и-запись-ячеек) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.4.3. Копирование + +Ассоциативный массив – это всего лишь ссылка с поверхностным копированием: при копировании или присваивании ассоциативных массивов создаются только новые псевдонимы для тех же данных внутри. Например: + +```d +auto a1 = [ "Jane":10.0, "Jack":20, "Bob":15 ]; +auto a2 = a1; // a1 и a2 ссылаются на одни данные +a1["Bob"] = 100; // Изменяя a1,... +assert(a2["Bob"] == 100); // ...мы изменяем a2... +a2["Sam"] = 3.5; // ...и +assert(a1["Sam"] == 3.5); // наоборот +``` + +При этом у ассоциативных массивов, как и у обычных, есть свойство `.dup`, создающее поэлементную копию массива. + +[В начало ⮍](#4-4-3-копирование) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.4.4. Проверка на равенство + +Операторы `is`, `==` и `!=` работают так, как и можно было ожидать. Для двух ассоциативных массивов `a` и `b` одного и того же типа выражение `a is b` истинно тогда и только тогда, когда переменные `a` и `b` ссылаются на один и тот же ассоциативный массив (то есть одной из переменных было присвоено значение другой). Выражение `a == b` поочередно сравнивает пары ключ–значение двух массивов с помощью оператора `==`. Чтобы `a` и `b` были равны, необходимо, чтобы в них совпали все ключи и значения для этих ключей. + +```d +auto a1 = [ "Jane":10.0, "Jack":20, "Bob":15 ]; +auto a2 = [ "Jane":10.0, "Jack":20, "Bob":15 ]; +assert(a1 !is a2); +assert(a1 == a2); +a2["Bob"] = 18; +assert(a1 != a2); +``` + +[В начало ⮍](#4-4-4-проверка-на-равенство) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.4.5. Удаление элементов + +Чтобы удалить из таблицы соответствий пару ключ–значение, передайте ключ в метод `remove`, имеющийся у каждого ассоциативного массива. + +```d +auto aa = [ "здравствуй":1, "до свидания":2 ]; +aa.remove("здравствуй"); +assert("здравствуй" !in aa); +aa.remove("эй"); // Ничего не происходит, т. к. в массиве aa нет ключа "эй" +``` + +Метод `remove` возвращает логическое значение: `true`, если удаленный ключ присутствовал в массиве, иначе – `false`. + +[В начало ⮍](#4-4-5-удаление-элементов) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.4.6. Перебор элементов + +Вы можете перебирать элементы ассоциативного массива с помощью старой доброй конструкции `foreach` (см. раздел 3.7.5). Пары ключ–значение просматриваются без определенного порядка: + +```d +import std.stdio; + +void main() +{ + auto coffeePrices = [ + "французская ваниль" : 262, + "ява" : 239, + "французская обжарка" : 224 + ]; + foreach (kind, price; coffeePrices) + { + writefln("%s стоит %s руб. за 100 г", kind, price); + } +} +``` + +Эта программа печатает стоимость разных сортов кофе: + +```sh +французская ваниль стоит 262 руб. за 100 г +ява стоит 239 руб. за 100 г +французская обжарка стоит 224 руб. за 100 г +``` + +Свойство `.keys` массива позволяет скопировать сразу все ключи из этого массива. Для любого ассоциативного массива `aa` типа `V[K]` выражение `aa.keys` возвращает тип `K[]`. + +```d +auto gammaFunc = [-1.5:2.363, -0.5:-3.545, 0.5:1.772]; +double[] keys = gammaFunc.keys; +assert(keys == [ -1.5, 0.5, -0.5 ]); +``` + +Аналогично для любого ассоциативного массива `aa` свойство `aa.values` возвращает все значения из `aa` в виде массива типа `V[]`. В общем случае для перебора элементов ассоциативного массива предпочтительно использовать цикл `foreach`, а не свойства `.keys` и `.values`, так как обращение к любому из этих свойств требует выделения памяти под новый массив, причем довольно большого объема в случае больших ассоциативных массивов. + +Есть два метода, позволяющих организовать перебор ключей и значений ассоциативного массива, не создавая новые массивы: с помощью выражения `aa.byKey()` можно просмотреть только ключи ассоциативного массива `aa`, а с помощью выражения `aa.byValue()` – только значения этого массива. Например: + +```d +auto gammaFunc = [-1.5:2.363, -0.5:-3.545, 0.5:1.772]; +// Вывести все ключи +foreach (k; gammaFunc.byKey()) +{ + writeln(k); +} +``` + +[В начало ⮍](#4-4-6-перебор-элементов) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +### 4.4.7. Пользовательские типы + +Ассоциативные массивы организованы так, что для обеспечения быстрого поиска используют хеширование и сортировку ключей. Чтобы использовать пользовательский тип для ключей ассоциативного массива, для него необходимо определить два специальных метода: `toHash` и `opCmp`. Мы еще не научились определять собственные типы и методы, поэтому отложим этот разговор до главы 6. + +[В начало ⮍](#4-4-7-пользовательские-типы) [Наверх ⮍](#4-массивы-ассоциативные-массивы-и-строки) + +## 4.5. Строки + +К строкам в D особое отношение. Два решения, принятые на ранней ста +дии развития языка (еще при его определении), оказались выигрышны +ми. Во-первых, в качестве своего стандартного набора знаков D принял +Юникод. (А Юникод сегодня – самый популярный и всеобъемлющий +стандарт определения и представления текстовых данных.) Во-вторых, +D использует кодировки UTF-8, UTF-16 и UTF-32, не отдавая предпоч +тения ни одной из них и не препятствуя использованию в вашем коде +любой другой кодировки. + +Чтобы понять, как D работает с текстом, нужно кое-что знать о Юнико +де и UTF. Если хотите изучить эти предметы в полном объеме, книга +«Unicode Explained» послужит вам полезным источником инфор +мации, а документированный стандарт «Консорциума Юникода» – +сейчас в пятом издании, что соответствует версии 5.1 стандарта Юни +код, – самая полная и точная справка по стандарту. + +### 4.5.1. Кодовые точки + +Нужно пояснить: Юникод различает «абстрактный знак», или *кодовую точку* (*code point*), и его представление, или *кодировку* (*encoding*). Об +этой тонкости мало кто знает, отчасти потому, что в стандарте ASCII нет +такого отдельного представления. Старый добрый стандарт ASCII каж- +дому знаку, часто встречающемуся в англоязычных текстах, (и каждо- +му из немногих «управляющих кодов») ставит в соответствие число +в диапазоне от 0 до 127, то есть 7 бит. Когда был предложен стандарт +ASCII, большинство компьютеров уже использовали 8-битный байт (ок- +тет) в качестве адресуемой единицы, и вопрос о «кодировании» ASCII- +текста не стоял. (Оставшийся бит оставлял простор для творчества, что +закончилось «Кембрийским взрывом»[^5] взаимно несовместимых расши- +рений.) + +Юникод же, напротив, сначала определяет кодовые точки, то есть, по +просту говоря, числа, поставленные в соответствие абстрактным зна +кам. Абстрактный знак «A» получает номер 65, абстрактный знак ¤ – +номер 8364 и т. д. Принятие решений о том, какие знаки заслуживают +быть включенными в таблицу знаков Юникода и как присваивать им +номера, – одно из важных дел, которыми занимается организация «Кон +сорциум Юникода». И это здорово, потому что все могут использовать +установленное ею соответствие между абстрактными знаками и чис +лами, не беспокоясь о таких мелочах, как его определение и докумен +тирование. + +По версии стандарта Юникод 5.1 кодовые точки Юникод находятся +в диапазоне от 0 до 1 114 111 (верхний предел гораздо чаще приводят +в шестнадцатеричном представлении: `0x10FFFF`, или `U+10FFFF` – в особой +юникодовской форме записи). Возможно, обычное заблуждение о том, +что двумя байтами можно представить любой из знаков таблицы Юни +кода, столь распространено из-за того, что некоторые языки приняли +в качестве стандарта двухбайтное представление знаков (что, в свою +очередь, стало следствием именно такого представления в более ранних +версиях стандарта Юникод). На самом деле, число знаков Юникод ров +но в 17 раз превышает 65 536 (максимальное число, доступное для двух +байтного представления). (По правде говоря, кодовые точки с большими значениями практически не используются, а многие из них вообще по +ка не имеют представления.) + +В любом случае, когда дело касается кодовых точек, можно не думать +об их представлении. Отвлеченно можно считать кодовые точки огром +ной таблицей значений функции, ставящей в соответствие целым чис +лам от 0 до 1 114 111 абстрактные знаки. Порядок назначения номеров +из этого диапазона имеет множество нюансов, но это не умаляет пра +вильности нашего высокоуровневого описания. О конкретном пред +ставлении кодовой точки из таблицы Юникод в виде последовательно +сти байтов позаботится *кодировка*. + +### 4.5.2. Кодировки + +Если бы Юникод не задумываясь последовал общему подходу ASCII, он +бы просто расширил верхнюю границу `0x10FFFF` до следующего байта, +чтобы каждая кодовая точка представлялась бы тремя байтами. Но +у такого решения есть определенный недостаток. В большинстве тек +стов на английском или другом языке с основанным на латинице алфа +витом была бы задействована статистически очень малая часть от обще +го количества кодовых точек (чисел), то есть память тратилась бы пона +прасну. Размер обычных текстов на латинице просто-напросто вырос +бы втрое. Алфавиты с большим количеством знаков (такие как азиат +ские системы письменности) нашли бы трем байтам лучшее примене +ние, и это нормально, ведь в целом в тексте было бы меньше знаков (но +каждый знак был бы более информативен). + +Чтобы не занимать лишнее место, Юникод принял несколько схем ко +дирования с *переменной длиной* представления знаков. Такие схемы ис +пользуют один или несколько «более узких» кодов для представления +всего диапазона кодовых точек Юникода. Узкие коды (обычно 8- или +16-битные) называются *кодовыми единицами* (*code units*). Каждая ко +довая точка представляется одной или несколькими кодовыми едини +цами. + +Первой стандартизированной кодировкой, работающей по этому прин +ципу, стала кодировка UTF-8. UTF-8, которую Кен Томпсон придумал +однажды вечером в небольшом ресторанчике в Нью-Джерси, – поч +ти образцовый пример оригинального и надежного решения. Основная +идея UTF-8: использовать для кодирования любого заданного знака от +1 до 6 байт; добавлять управляющие биты, по которым можно будет +различать представления знаков разной длины. Представления пер +вых 127 кодовых точек в кодировке UTF-8 идентичны представлениям +в ASCII. То есть все ASCII-тексты автоматически становятся коррект +ными с точки зрения UTF-8, что само по себе блестящий ход. Для кодо +вых точек, не входящих в диапазон ASCII, UTF-8 использует представ +ления разной длины (табл. 4.2). + +*Таблица 4.2. Битовые представления UTF-8. Длина представления определяется по контрольным битам, что позволяет выполнять синхронизацию посреди потока, восстановление после ошибок и просмотр строки в обратном направлении* + +|Кодовая точка (в шестнадцатиричном представлении)|Бинарное представление| +|-|-| +|`00000000–0000007F`|`0xxxxxxx`| +|`00000080–000007FF`|`110xxxxx 10xxxxxx`| +|`00000800–0000FFFF`|`1110xxxx 10xxxxxx 10xxxxxx`| +|`00010000–001FFFFF`|`11110xxx 10xxxxxx 10xxxxxx 10xxxxxx`| +|`00200000–03FFFFFF`|`111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx`| +|`04000000–7FFFFFFF`|`1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx`| + +Поскольку на сегодня верхней границей диапазона кодовых точек Юни +код является число `0x10FFFF`, две последние последовательности зарезер +вированы для использования в будущем; в настоящее время корректны +только четырехбайтные представления. + +Выбранные последовательности управляющих битов обладают двумя +любопытными свойствами: +1. Первый байт представления всегда отличается от остальных его бай +тов. +2. Первый байт однозначно определяет длину представления. + +Первое свойство является ключевым, так как находит два важных при +менения. Первое – простая синхронизация: начав принимать переда +ваемую информацию в кодировке UTF-8, прямо посреди потока легко +можно выяснить, где начинается представление следующей кодовой +точки (просто найдите следующий байт, начинающийся не с 10). Вто +рое применение этого свойства – просмотр в обратном направлении: по +строке UTF-8 можно легко перемещаться от конца к началу, не сбива +ясь. Возможность просматривать строки UTF-8 в обратном направле +нии позволяет организовать множество алгоритмов (например, эффек +тивный поиск последнего вхождения одной строки в другую). Второе +свойство последовательностей управляющих битов не столь значимо, +но оно упрощает и ускоряет обработку строк. + +В идеале часто встречающиеся кодовые точки должны иметь малые +представления, а редко встречающиеся – большие. При таких услови +ях кодировка UTF-8 работает как хороший статистический кодиров +щик, обозначая более часто встречающиеся знаки меньшим количест +вом битов. Это удобно для языков с алфавитом на основе латиницы, где +для большинства букв достаточно одного байта, а для редких букв с ди +акритическими знаками – двух. + +UTF-16 – тоже кодировка с переменной длиной, но в ней применяется +другой (пожалуй, менее элегантный) подход к кодированию. Кодовые +точки со значениями в диапазоне от `0` до `0xFFFF` кодируются одной 16-бит +ной кодовой единицей, а кодовые точки со значениями в диапазоне от +`0x10000` до `0x10FFFF` представляются *суррогатными парами*, то есть дву +мя кодовыми единицами, первая из которых находится в диапазоне от +`0xD800` до `0xDBFF`, а вторая – в диапазоне от `0xDC00` до `0xDFFF`. Ради этой ко +дировки Юникод отказался от отображения кодовых точек на значения +в диапазоне `0xD800–0xDBFF`. Диапазоны значений первой и второй кодо +вых единиц называются *верхней суррогатной зоной* (*high surrogate area*) +и *нижней суррогатной зоной* (*low surrogate area*) соответственно. + +Обычно UTF-16 критикуют за то, что в этой кодировке статистически +редкие случаи также становятся наиболее сложными в обработке и тре +буют самого тщательного рассмотрения. К сожалению, не все, но боль +шинство знаков Юникода – так называемая базовая многоязыковая +плоскость (Basic Multilingual Plane, BMP) – действительно могут быть +закодированы единственной кодовой единицей кодировки UTF-16, по +этому множество программ, работающих с UTF-16, автоматически при +нимают одну кодовую единицу за представление одного знака, отказы +ваясь от проверок на наличие суррогатных пар в пользу эффективности. +Еще больше усугубляет путаницу то, что некоторые языки изначально +выбрали поддержку предшественницы UTF-16 – кодировки UCS-2 (в ко +торой одной кодовой точке соответствуют ровно 16 бит), а позже реши +ли добавить поддержку UTF-16, что осложнило использование старого +кода, полагающегося на соответствие между знаками и их кодовыми +единицами вида один-к-одному. + +Наконец, кодировка UTF-32 использует 32 бита для одной кодовой еди +ницы. Это означает, что в кодировке UTF-32 принято самое простое +и легкое в использовании, но в то же время самое «прожорливое» пред +ставление кодовых точек. Обычно рекомендуют придерживаться сле +дующей политики: кодировку UTF-8 использовать для хранения, а к ко +дировке UTF-32 обращаться лишь временно, во время обработки, и толь +ко при необходимости. + +### 4.5.3. Знаковые типы + +В языке D определено три знаковых типа: `char`, `wchar` и `dchar`, обозначаю +щие кодовые единицы кодировок UTF-8, UTF-16 и UTF-32 соответствен +но. В качестве значений свойства `.init` этих типов намеренно выбраны +некорректные значения: `char.init` равно `0xFF`, `wchar.init` – `0xFFFF`, а `dchar.init` – `0x0000FFFF`. + +Из табл. 4.2 видно, что константа `0xFF` не может быть частью корректно +го битового представления знака в кодировке UTF-8, а значению `0xFFFF` +Юникод намеренно не ставит в соответствие никакую кодовую точку. + +Используемые по отдельности, значения этих трех знаковых типов ве +дут себя в основном как целые числа без знака и иногда могут использо +ваться для хранения некорректных UTF-представлений кодовых точек +(компилятор не заботится о том, чтобы везде использовались коррект +ные представления кодовых точек), но изначально задуманное назначе +ние типов `char`, `wchar` и `dchar` – служить UTF-представлениями кодовых +точек. А для работы с 8-, 16- и 32-битными целыми числами без знака +и для представления кодировок, не входящих в группу UTF, лучше все +го использовать типы `ubyte`, `ushort` и `uint` соответственно. Например, +для работы с применявшимися до появления Юникода 8-битными ко +довыми страницами вы можете взять за основу значения типа `ubyte`, +а не `char`. + +### 4.5.4. Массивы знаков + бонусы = строки + +Сформированный массив любого знакового типа – такого как `char[]`, +`wchar[]` или `dchar[]` – компилятор и библиотека средств поддержки вре +мени исполнения считают строками Юникода в одной из UTF-кодиро +вок. Следовательно, массивы знаков сочетают в себе мощь и гибкость, +свойственные массивам, и некоторые дополнительные преимущества, +предоставляемые Юникодом. + +На самом деле, в D уже определены три типа строк, соответствующие +трем размерам представления знаков: `string`, `wstring` и `dstring`. Это не +особые типы, а всего лишь псевдонимы массивов знаковых типов с од +ним отличием: знаковый тип снабжен квалификатором `immutable`, за +прещающим произвольное изменение отдельных знаков в строке. На +пример, тип `string` – более короткий синоним для типа `immutable(char)[]`. +Подробное обсуждение квалификаторов типов (в том числе `immutable`) +мы отложим до главы 8, но для строк в любой кодировке действие `immutable` объясняется очень просто: свойства значения типа `string`, также +известного как `immutable(char)[]`, идентичны свойствам значения типа +`char[]` (а свойства значения типа `immutable(wchar)[]` – свойствам значения +типа `wchar[]`), за исключением маленького отличия: нельзя присвоить +новое значение отдельному знаку строки: + +```d +string a = "hello"; +char h = a[0]; // Все в порядке +a[0] = 'H'; // Ошибка! Присваивать типу immutable(char) запрещено! +``` + +Чтобы изменить в строке какой-то конкретный знак, требуется создать +новое значение типа `string`, применив конкатенацию: + +```d +string a = "hello"; +a = 'H' ~ a[1 .. $]; // Все в порядке, делает выражение a == "Hello" истинным +``` + +Почему было принято такое решение? В конце концов в приведенном +выше примере совершенно бессмысленно выделять новую область памя +ти под целую строку (вспомните, в разделе 4.1.6 говорилось, что опера +тор `~` всегда требует выделения новой области памяти под новый массив), +вместо того чтобы просто изменить уже имеющуюся строку. В пользу +квалификатора `immutable` говорит то, что его наличие упрощает ситуа +ции, когда объект типа `string`, `wstring` или `dstring` копируется, а потом +изменяется. Квалификатор гарантирует отсутствие лишних ссылок на +одну и ту же строку. Например: + +```d +string a = "hello"; +string b = a; // Переменная b теперь тоже указывает на значение "hello" +string c = b[0 .. 4]; // Переменная c указывает на строку "hell" +// Если бы такое присваивание было разрешено, это изменило бы a, b, и c: a[0] = 'H'; +// Конкатенация оставляет переменные b и c нетронутыми: +a = 'H' ~ a[1 .. $]; +assert(a == "Hello" && b == "hello" && c == "hell"); +``` + +Неизменяемость отдельных знаков позволяет работать с несколькими +переменными, ссылающимися на одну и ту же строку, не боясь, что из +менение одной из них отразится и на других. Копировать строковые +объекты очень дешево, поскольку не реализуется никакая особая стра +тегия копирования (например, раннее копирование или копирование +при записи). + +Не менее весомая причина запретить изменения в строках на уровне ко +довых единиц – такие изменения все равно лишены смысла. Элементы +`string` имеют разную длину, а в большинстве случаев требуется заменить +логические знаки (кодовые точки), а не физические (кодовые единицы), +поэтому желание проводить хирургические операции над отдельными +знаками возникает редко. Гораздо легче записать правильный UTF-код, +отказавшись от присваивания отдельным знакам, но уделив больше +внимания работе с целыми строками и их фрагментами. Стандартная +библиотека D задает тон, поддерживая работу со строками как с едины +ми сущностями (а не с индексами и отдельными знаками). Тем не менее +писать UTF-код не так легко; например, в предыдущем примере в кон +катенации `'H' ~ a[1 .. $]` допущена ошибка: эта запись предполагает, что +первая кодовая точка занимает ровно один байт. Правильное решение +выглядит так: + +```d +a = 'H' ~ a[stride(a, 0) .. $]; +``` + +Функция `stride` из модуля `std.utf` стандартной библиотеки возвращает +длину кода знака в указанной позиции строки. Для доступа к функции +`stride` и другому полезному содержимому библиотеки вставьте где-ни +будь ближе к началу программы строку: + +```d +import std.utf; +``` + +В нашем случае вызов `stride(a, 0)` возвращает количество байт двоич +ного представления первого знака (кодовой точки) в строке `a`. Именно +это число мы используем при получении среза, помечая начало второго +знака. + +Наглядный пример поддержки Юникода языком можно обнаружить +в строковых литералах, с которыми мы уже успели познакомиться (см. +раздел 2.2.5). Строковые литералы D «понимают» кодовые точки из +таблицы Юникод и автоматически кодируют их в соответствии с любой +выбранной вами кодировкой. Например: + +```d +import std.stdio; + +void main() +{ + string a = "Независимо от представления \u03bb стоит \u20AC20."; + wstring b = "Независимо от представления \u03bb стоит \u20AC20."; + dstring c = "Независимо от представления \u03bb стоит \u20AC20."; + writeln(a, '\n', b, '\n', c); +} +``` + +Несмотря на то что внутренние представления строк `a`, `b` и `c` сильно от +личаются друг от друга, вам не нужно об этом беспокоиться, потому что +вы задаете литерал в абстрактном виде, используя кодовые точки. Ком +пилятор заботится обо всех тонкостях кодирования, так что в итоге +программа печатает три строки с одним и тем же текстом: + +```d +Независимо от представления λ стоит €=20. +``` + +Кодировка литерала определяется контекстом, в котором этот литерал +используется. В предыдущем примере компилятор преобразует строко +вый литерал, без какой-либо обработки во время исполнения програм +мы, из кодировки UTF-8 в кодировку UTF-16, а потом в кодировку +UTF-32 (соответствующие типам `string`, `wstring` и `dstring`), хотя написа +ние литералов во всех трех случаях одинаково. Если требуемая кодиров +ка литерала не может быть однозначно определена, добавьте к нему суф +фикс `c`, `w` или `d` (например, `"как_здесь"d`): строка будет преобразована в ко +дировку UTF-8, UTF-16 или UTF-32 соответственно (см. раздел 2.2.5.2). + +#### 4.5.4.1. Цикл foreach применительно к строкам + +Если просматривать строку `str` (в любой кодировке) таким способом: + +```d +foreach (c; str) +{ + ... // Использовать c +} +``` + +то переменная `c` поочередно примет значение каждой из *кодовых единиц* строки `str`. Например, если `str` – массив элементов типа `char` (с ква +лификатором `immutable` или без), то переменной c присваивается тип +`char`. Это ожидаемо, если вспомнить, как ведет себя цикл просмотра +с массивами, но иногда для строк такое поведение нежелательно. На +пример, напечатаем знаки строки типа `string`, заключив каждый из +них в квадратные скобки. + +```d +void main() +{ + string str = "Hall\u00E5, V\u00E4rld!"; + foreach (c; str) + { + write('[', c, ']'); + } + writeln(); +} +``` + +Но напечатает эта программа совсем не то, что ожидалось: + +```sh +[H][a][l][l][�][�][,][ ][V][�][�][r][l][d][!] +``` + +Негатив знака `�` (может отличаться в зависимости от операционной сис +темы и используемого шрифта) – это немой протест консоли против отображения некорректного UTF-кода. Разумеется, попытка напечатать отдельный элемент типа `char`, обретающий смысл только в сочетании с другими элементами типа `char`, обречена на провал. + +Но самое интересное начинается, если вы укажете для `c` другой знако +вый тип. Например, назначим переменной `c` тип `dchar`: + +```d +...тот же самый код, добавлен только тип "dchar"... +foreach (dchar c; str) +{ + write('[', c, ']'); +} +``` + +В этом случае компилятор автоматически вставляет код для перекоди +ровки «на лету» каждой кодовой единицы в `str` в представление, дик +туемое типом переменной `c`. Наш цикл напечатает: + +```sh +[H][a][l][l][å][,][ ][V][ä][r][l][d][!] +``` + +а это указывает на то, что каждый из двухбайтных знаков `å` и `ä` был правильно преобразован к соответствующему знаку типа `dchar`, и поэтому они были напечатаны верно. То же самое будет напечатано, если задать для переменной `c` тип `wchar`, поскольку указанные в литерале два знака, отсутствующие в таблице ASCII, вмещаются в единственную кодовую единицу кодировки UTF-16, но это не общий случай (суррогатные пары будут обработаны неверно). Однако чтобы обеспечить максимально возможную степень безопасности, конечно же, лучше всего при просмотре строк использовать тип `dchar`. + +В рассмотренном примере в инструкции `foreach` выполнялось перекодирование в направлении от «узкого» к более «широкому» представлению, но обратное преобразование также возможно. Например, можно начать со значения типа `dstring`, а затем просмотреть его по одному (закодированному) знаку типа `char`. + +## 4.6. Опасный собрат массива – указатель + +Объект массива отслеживает в памяти группу типизированных объек +тов, сохраняя адреса ее верхней и нижней границ. Указатель – «наполо +вину массив»: он позволяет отслеживать только один объект. Поэтому +указатель не знает, где начинается и заканчивается группа объектов. +Если вы получите эту информацию откуда-то извне, то сможете исполь +зовать ее для организации перемещения указателя, заставляя его ука +зывать на соседние элементы. + +Указатель на объект типа `T` обозначается как тип `T*` и по умолчанию +имеет значение `null` (то есть указывает «в никуда»). Направить указа +тель на объект можно с помощью оператора получения адреса `&`, а ис +пользовать этот объект – с помощью оператора разыменования `*` (см. +раздел 2.3.6.2). Например: + +```d +int x = 42; +int* p = &x; // Получить адрес x +*p = 10; // *p можно использовать там же, где и x +++*p; // Обычные операторы также применимы +assert(x == 11); // Переменная x была изменена с помощью указателя p +``` + +Указатели могут участвовать в арифметических операциях, что делает +чрезвычайно заманчивым их применение в качестве курсоров внутри +массивов. Если увеличить указатель на единицу, он будет указывать на +следующий элемент массива, если уменьшить на единицу – на предыду +щий элемент. Прибавив к указателю целое число `n`, получим указатель +на объект, отстоящий от элемента, на который указывал исходный ука +затель, на `n` позиций вправо, если `n` больше нуля, и влево, если `n` меньше +нуля. Ради упрощения операции индексирования выражение `p[n]` экви +валентно выражению `*(p + n)`. Наконец, разница между двумя указате +лями `p2 - p1` соответствует такому целому числу `n`, что `p1 + n == p2`. + +Можно получить адрес первого элемента массива `arr` с помощью вы +ражения вида `arr.ptr`. Следовательно указатель на последний элемент +непустого массива arr можно получить с помощью выражения `arr.ptr + arr.length - 1`, а указатель на область памяти сразу за последним эле +ментом массива – с помощью выражения `arr.ptr + arr.length`. Проиллю +стрируем все сказанное примером: + +```d +auto arr = [ 5, 10, 20, 30 ]; +auto p = arr.ptr; +assert(*p == 5); +++p; +assert(*p == 10); +++*p; +assert(*p == 11); +p += 2; +assert(*p == 30); +assert(p - arr.ptr == 3); +``` + +Однако будьте осторожны: если вы не обладаете информацией об адре +сах границ массива (указатель вам об этом не сообщит, а значит, это +должно быть известно откуда-то еще), ситуация вскоре может стать не +предсказуемой. Никакие операции с участием указателей не проверя +ются: указатель – это всего лишь адрес памяти длиной в слово[^6], и ариф +метические операции, которые вы к нему применяете, просто слепо ис +полняют то, о чем вы просите. Это делает указатели невероятно быст +рыми и при этом ужасно неосведомленными. Указатель недостаточно +умен даже для того, чтобы понять, что он указывает на отдельный объ +ект (в отличие от указания на элемент массива): + +```d +auto x = 10; +auto y = &x; +++y; // Хм... +``` + +Указателю также неизвестно, когда он вышел за границу массива: + +```d +auto x = [ 10, 20 ]; +auto y = x.ptr; +y += 100; // Хм... +*y = 0xdeadbeef; // Русская рулетка +``` + +Присваивать значение с помощью указателя, который не указывает на +корректные данные, – значит играть в русскую рулетку с целостностью +своей программы: записи могут «приземлиться» где угодно, растоптав +самые тщательно оберегаемые данные, а то и код. Все это делает указа +тели *небезопасным для памяти* (*memory-unsafe*) средством. + +Поэтому старательно избегайте указателей, отдавая предпочтение мас +сивам, ссылкам на классы (см. главу 6), аргументам функций, передан +ным с ключевым словом `ref` (см. раздел 5.2.1), и автоматическому управ +лению памятью. Все эти средства безопасны, могут эффективно прове +ряться и почти не снижают быстродействие. + +В действительности, массивы – весьма полезная абстракция, тщатель +но спроектированная с единственною целью: *создать самое быстрое после указателей средство с учетом ограничений безопасности для памяти*. Очевидно, что сам по себе указатель не имеет доступа к доста +точному количеству информации, чтобы выяснить что-то самостоя +тельно; массив, напротив, знает свой размер, поэтому может легко про +верять, что все операции над ними совершаются в пределах границ рас +положения данных. + +С точки зрения высокого уровня можно отметить, что массивы слиш +ком низкоуровневые и что их реализация недотягивает до абстрактно +го типа данных. С другой стороны, если мыслить низкоуровневыми ка +тегориями, может показаться, что в массивах нет необходимости, так +как они могут быть реализованы с помощью указателей. Ответ на оба +эти аргумента против массивов все тот же: «Я все объясню». + +Ценность массивов в том, что из всех абстракций эта абстракция – са +мая низкоуровневая, но при этом она уже безопасна. Если бы язык пре +доставлял только указатели, то был бы не способен обеспечить безопас +ность различных пользовательских конструкций более высокого уров +ня, построенных на базе указателей. Массивы также не должны быть +слишком высокоуровневыми, потому что являются встроенными, а зна +чит, все остальное будет создаваться, используя их как основу. Хорошее +встроенное средство должно быть низкоуровневым и быстрым, чтобы +можно было строить на его базе абстракции более высокого уровня, не +обязательно столь же быстрые. Именно так и развиваются абстракции. + +Существует «урезанная» безопасная версия D, известная как SafeD (см. +главу 11), также есть флаг компилятора, установка которого включает +проверку принадлежности используемых в программе инструкций и ти +пов данных этому безопасному подмножеству средств языка. Естест +венно, в безопасном D (SafeD) запрещено большинство операций с ука +зателями. Встроенные массивы – это важное средство, позволяющее +создавать мощные, выразительные программы на SafeD. + +## 4.7. Итоги и справочник + +В табл. 4.3 собрана информация об операциях над динамическими мас +сивами, в табл. 4.4. – об операциях над массивами фиксированной дли +ны, а в табл. 4.5 – об операциях над ассоциативными массивами. + +*Таблица 4.3. Операции над динамическими массивами (`a` и `b` – два значения типа `T[]`; `t`, `t1`, ..., `tk` – значения типа `T`; `n` – значение, приводимое к типу `размер_t`)* + +|Выражение|Тип|Описание| +|-|-|-| +|`new T[n]`|`T[]`|Создает массив ([раздел 4.1](../chapter-4-1/))| +|`[t1,t2, ..., tk]`|`T[]`|Литерал массива; `T` определяется по типу `t1` (разделы [2.2.6](../../2/chapter-2-2-6/) и [4.1](../chapter-4-1/))| +|`a = b`|`T[]`|Присваивает один массив другому ([раздел 4.1.4](../chapter-4-1-4/))| +|`a[<в>]`|`ref T`|Предоставляет доступ к элементу по индексу (символ `$` в выражении `<в>` заменяется на `a.length`, `<в>` должно быть приводимым к типу `размер_t`; кроме того, должно соблюдаться условие `<в> < a.length`) ([раздел 4.1](../chapter-4-1/))| +|`a[<в1> .. <в2>]`|`T[]`|Получает срез массива `a` (знак `$` в `<в1>` и `<в2>` заменяется на `a.length`, `<в1>` и `<в2>` должны быть приводимыми к типу `размер_t`, также должно соблюдаться условие `<в1> <= <в2> && <в2> <= a.length`) ([раздел 4.1.3](../chapter-4-1-3/))| +|`a[]`|`T[]`|Поэлементная операция ([раздел 4.1.7](../chapter-4-1-7/)) или альтернативное написание выражения `a[0 .. $]`, возвращающего содержимое всего массива| +|`a.dup`|`T[]`|Получает дубликат массива ([раздел 4.1](../chapter-4-1/))| +|`a.length`|`размер_t`|Читает длину массива ([раздел 4.1.10](../chapter-4-1-10/))| +|`a.length = n`|`размер_t`|Изменяет длину массива ([раздел 4.1.1](../chapter-4-1-1/))| +|`a is b`|`bool`|Проверяет, идентичны ли массивы друг другу (разделы [4.1.5](../chapter-4-1-5/) и [2.3.4.3](../../2/chapter-2-3-4-3/))| +|`a !is b`|`bool`|То же, что `!(a is b)`| +|`a == b`|`bool`|Поэлементно сравнивает массивы на равенство ([раздел 4.1.5](../chapter-4-1-5/))| +|`a != b`|`bool`|То же, что `!(a == b)`| +|`a ~ t`|`T[]`|Конкатенирует массив и отдельное значение ([раздел 4.1.6](../chapter-4-1-6/))| +|`t ~ a`|`T[]`|Конкатенирует отдельное значение и массив ([раздел 4.1.6](../chapter-4-1-6/))| +|`a ~ b`|`T[]`|Конкатенирует два массива ([раздел 4.1.6](../chapter-4-1-6/))| +|`a ~= t`|`T[]`|Присоединяет элемент к массиву ([раздел 4.1.6](../chapter-4-1-6/))| +|`a ~= b`|`T[]`|Присоединяет один массив к другому ([раздел 4.1.6](../chapter-4-1-6/))| +|`a.ptr`|`T*`|Возвращает адрес первого элемента массива `a` (небезопасная операция) ([раздел 4.6](../chapter-4-6/))| + +*Таблица 4.4. Операции над массивами фиксированной длины (`a` и `b` – два значения типа `T[]`; `t`, `t1`, ..., `tk` – значения типа `T`; `n` – значение, приводимое к типу `размер_t`)* + +|Выражение|Тип|Описание| +|-|-|-| +|`[t1, ..., tk]`|`T[k]`|Литерал массива, но только если тип `T[k]` запрошен явно; `T` определяется по типу `t1` (разделы [2.2.6](../../2/chapter-2-2-6/) и [4.1](../chapter-4-1/))| +|`a = b`|`ref T[n]`|Копирует содержимое одного массива в другой ([раздел 4.2.4](../chapter-4-2-4/))| +|`a[<в>]`|`ref T`|Предоставляет доступ к элементу по индексу (символ `$` в `<в>` заменяется на `a.length`, `<в>` должно быть приводимым к типу `размер_t`; кроме того, должно соблюдаться условие `<в> < a.length`) ([раздел 4.1](../chapter-4-1/))| +|`a[<в1> .. <в2>]`|`T[]/T[k]`|Получает срез массива `a` (символ `$` в `<в1>` и `<в2>` заменяется на `a.length`, `<в1>` и `<в2>` должны быть приводимыми к типу `размер_t`, также должно соблюдаться условие `<в1> <= <в2> && <в2> <= a.length`) ([раздел 4.2.3](../chapter-4-2-3/))| +|`a[]`|`T[]`|Поэлементная операция ([раздел 4.1.7](../chapter-4-1-7/)) или приведение `a` (массива фиксированной длины) к типу динамического массива, то же, что и `a[0 .. $]`| +|`a.dup`|`T[]`|Получает дубликат массива ([раздел 4.2.4](../chapter-4-2-4/))| +|`a.length`|`размер_t`|Читает длину массива ([раздел 4.2.1](../chapter-4-2-1/))| +|`a is b`|`bool`|Проверяет, идентичны ли массивы друг другу (разделы [4.2.5](../chapter-4-2-5/) и [2.3.4.3](../../2/chapter-2-3-4-3/))| +|`a !is b`|`bool`|То же, что и `!(a is b)`| +|`a == b`|`bool`|Поэлементно сравнивает массивы на равенство (разделы [4.2.5](../chapter-4-2-5/) и [2.3.12](../../2/chapter-2-3-12-1/))| +|`a != b`|`bool`|То же, что и `!(a == b)`| +|`a ~ t`|`T[]`|Конкатенирует массив и отдельное значение ([раздел 4.2.6](../chapter-4-2-6/))| +|`t ~ a`|`T[]`|Конкатенирует отдельное значение и массив ([раздел 4.2.6](../chapter-4-2-6/))| +|`a ~ b`|`T[]`|Конкатенирует два массива ([раздел 4.2.6](../chapter-4-2-6/))| +|`a.ptr`|`T*`|Возвращает адрес первого элемента массива `a` (небезопасная операция)| + +*Таблица 4.5. Операции над ассоциативными массивами (`a` и `b` – два значения типа `V[K]`; `k`, `k1`, ..., `ki` – значения типа `K`; `v`, `v1`, ..., `vk` – значения типа `V`)* + +|Выражение|Тип|Описание| +|-|-|-| +|`[t1:v1, ..., ti:vi]`|`V[K]`|Литерал ассоциативного массива; `K` определяется по типу `k1`, а `V` – по типу `v1` (разделы [2.2.6](../../2/chapter-2-2-6/) и [4.4](../chapter-4-4/))| +|`a = b`|`V[K]`|Присваивает ассоциативный массив `b` переменной a типа «ассоциативный массив» ([раздел 4.4.3](../chapter-4-4-3/))| +|`a[k]`|`V`|Предоставляет доступ к элементу по индексу (если ключ `k` не найден, возникает исключение) ([раздел 4.4.2](../chapter-4-4-2/))| +|`a[k] = v`|`V`|Ставит в соответствие ключу `k` значение `v` (переопределяет предыдущее соответствие, если оно уже было назначено) ([раздел 4.4.2](../chapter-4-4-2/))| +|`k in a`|`V*`|Ищет `k` в `a`, возвращает `null`, если не находит, иначе – указатель на значение, ассоциированное с `k` ([раздел 4.4.2](../chapter-4-4-2/))| +|`k !in a`|`bool`|То же, что и `!(k in a)`| +|`a.length`|`размер_t`|Читает значение, соответствующее числу элементов в `a` ([раздел 4.4.1](../chapter-4-4-1/))| +|`a is b`|`bool`|Проверяет, идентичны ли массивы друг другу (разделы [4.4.4](../chapter-4-4-4/) и [2.3.4.3](../../2/chapter-2-3-4-3/))| +|`a !is b`|`bool`|То же, что и `!(a is b)`| +|`a == b`|`bool`|Поэлементно сравнивает массивы на равенство (разделы [4.4.4](../chapter-4-4-4/) и [2.3.12](../../2/chapter-2-3-12-1/))| +|`a != b`|`bool`|То же, что и `!(a == b)`| +|`a.remove(k)`|`bool`|Удаляет пару с ключом `k`, если такая есть; возвращает `true`, если и только если ключ `k` присутствовал в `a` ([раздел 4.4.5](../chapter-4-4-5/))| +|`a.dup`|`V[K]`|Создает дубликат ассоциативного массива `a` ([раздел 4.4.3](../chapter-4-4-3/))| +|`a.get(k, v)`|`V`|Возвращает значение из `a`, соответствующее ключу `k`; по умолчанию возвращается значение `v` ([раздел 4.4.2](../chapter-4-4-2/))| +|`a.byKey()`|`int delegate(int delegate(ref K))`|Возвращает делегат, пригодный для использования в цикле `foreach` для итерации по ключам| +|`a.byValue()`|`int delegate(int delegate(ref V))`|Возвращает делегат, пригодный для использования в цикле `foreach` для итерации по значениям| + +[^1]: quadrupeds (англ.) – четвероногие. – *Прим. пер.* +[^2]: Заметим также, что переход по нужному индексу статического многомерного массива происходит за один раз, а сам массив хранится в непрерывной области памяти. Например, для хранения массива `arr` типа `int[5][5]` выделяется область размером `5 * 5 * int.sizeof` байт, а переход по адресу `arr[2][2]` выглядит как `&arr + 2 * 5 + 2`. Если же статический массив размещается в сегменте данных (как глобальная переменная или как локальная с атрибутом `static`), а индексы известны на этапе компиляции, то переход по указателю вообще не потребуется. – *Прим. науч. ред.* +[^3]: При этом для массива типа `V[K]` передаваемые ключи должны иметь тип `immutable(K)` или неявно приводимый к нему. Это требование введено для того, чтобы в процессе работы программы значение ключа не могло быть изменено косвенным образом, что повлекло бы нарушение структуры ассоциативного массива. – *Прим. науч. ред.* +[^4]: Как уже говорилось, оператор `in` возвращает указатель на элемент, соответствующий ключу, или `null`, если такой ключ отсутствует в массиве. – *Прим. науч. ред.* +[^5]: Кембрийский взрыв – неожиданное появление в раннекембрийских отложениях окаменелостей представителей многих подразделений животного царства на фоне отсутствия их окаменелостей или окаменелостей их предков в докембрийских отложениях. – *Прим. пер.* +[^6]: В архитектуре x86 тип указатель размером в 4 байта соответствует двойно +му слову (DW), а слову соответствует тип short размером 2 байта. – Прим. на +уч. ред. diff --git a/04-массивы-ассоциативные-массивы-и-строки/images/image-4-1-4-1.png b/04-массивы-ассоциативные-массивы-и-строки/images/image-4-1-4-1.png new file mode 100644 index 0000000000000000000000000000000000000000..6bb761dee0831637c9f3a23556b6911c408db8ed GIT binary patch literal 12665 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 NC#@`%FJbuMe*v5H7JvW% literal 0 HcmV?d00001 diff --git a/04-массивы-ассоциативные-массивы-и-строки/images/image-4-1-4-2.png b/04-массивы-ассоциативные-массивы-и-строки/images/image-4-1-4-2.png new file mode 100644 index 0000000000000000000000000000000000000000..70dd2f417529e6a64b8fed2b1060d8c1d16fb2fd GIT binary patch literal 15501 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⪼)vyiJ11+I!xCetxvyLGXb96|70E~Nb0A7&`q{F2eG=M&-Oe0 zWoDCA_IukfMIbU1a=B+1|2BA0EopB0a>C>UNIw5n9ms!Icls}DOaz(O$di(j_one7 z{a12B7EXccFJFGQjt2j`O3~Sw6A0_Tahs*Vf*1m|pl69eBnCO@Xm4*1RK$=~JRpLM zNs0AAx-HE;kOtaI$SM(NBECmt0-~T^ov*Isjlzs`_1L>wCl}STAb^6taWgS7d4HM$ z=p_vb-t?vk(bP_DwB0YaAh<6a2gGr29Oe~oxUur^49y}?GQan9T`hbtCk(UGSdcX7 zd`K5aj#xauijo2msSD=(v`|}Ha40%)B>Pcq5fHk{j(HD;DpEx~uZHg7{9 zzCtrnvdpnP$>NKE1BiI;Y>(7a7*7MuH2EGm+XkY<=lo<~;KK)to|JR z9xcw1i+sO15h{ZP$ya(kcAl@sT7{|0^_&&IG6on>X^|-+`o8lrUCie$MfLBV2sEhu zc%!mM#0JEE0LwJlkAD2OCg1H6SzWp=dV#FfjndSEuFu#jC={_ zgFMUq-M7vuCbq2#_MdPFy5Qes-N|8h*+O@S|BA&y0a9Gn%lDn3A3oremHDE80=!d= z>uOuEUH_~84!#tT@UH`5)QpR*>bST#@LZhEt~`7^%k^?sWk+}vfrc)!_eiA{26gyR)oJWFfe$zAVMc$?1O`IrD}$BvOUlLbo=^EP#6I?1k=?SgB-DSrvgQN zT)_ViL3)p6f^|nvhZybMJ*GU;P#Iv4K^a%`lETTdu!U0P`pC)l4al?FgYhgNeq3ub z4+n~jtgL8{Jm3VPXvKUl&MG}-)8pf1iDY_``6|)JoQ&~`KzrkjT8jWRYHGRWk{y0H z1iTJ|GW9$5sA*LA^k=11QQdk+W5Ix?rY8UXD*FjPG(}hdjYMfcfRV}_h#JOJ1w840 z$+ih*tFay)9)iLCQ>8$k&=D3ER;VTGm!tT%lA*o3JEm(H6saxLGi2(6iJFtL+w_x` zmM*rvDJALZn_g?(Gb3A&1yf{< zX}-kNfBEu7F`kV&!;q0!%x_U2$laqV#q6;KPk2XfPZr@t|iVb3_ zKn4whV0yZ`e0F0%mP~()xhUh{Sn^}UdPc!W+~ESJBc!=KyRda(UvA;+iBt1KJ^uuK zYWeEbn24akhsZF%*?um{0gWj$?!*3B2?jAkgyrS#e9J#|QBQP;&?oALl|?HYMxcW3 z#S3M8g4=^f<5*29ART#M*ve@*_l1aO$Q^3Y55WJ+2_|cX{!=Se1OpK%;_IgLM6aLd zle>x`m3@7?kM1GL%-piPb&h92OtA_;{%`dA53t*OOhR5iGT(yIH$-{$3h?+{3I zp~Cra_-u{BA}FW3e_#00jk&ERSx{&o#4RnEp9%LNi-2e6@6ZhNd-jAUW@SE*_|VV- zYhay2?$Gg@^k;rmPT8NPnWLo0dTC{4WpAIBlytf}zyJ_h`&HFc-{pk@k<4_RyZbbj z{-p4ANl~9OC`aslT+C@SorJdx(R&b<9}lMxzbGJ{VXld!bm!i8!+uPQt!B#ll=sT& zQT$*5)omdCR_EB4-b2Md`ptT}OtTGCRNdJK@Xv^Q<=`+=?#XGG(Flw?vqVah56a=s z_hoj(x2lSY^Tp#WV*y8I+?T7RrXYHkfq{Ywi=_aGjd}>AiI_U5Gn*;gvGq42BcoE1 z;K1+SVDG;F2CMV9PzBg{IKRwdYeI`%8)Qn@_7B&9&=1hnK>B1szNBiWwtD}V6T=U-@BrVTx3-Iy+dw~~}4XVnr>OCT;`NC2NY9eXMpS%hIS{SJ;qEHzi zcw~@^5cWQ=NlLmeKm&oCP&!rTGy%lbgu!eG*wM>1))*5F&W_hMm%D1b%r31No0*|? zz6SXrT+&qmsv80hwyLQ!;(BL(zHI|VcTfjb)?5GOVxs?N#X`knKZ#7^01xzDEk~+I zpCj*sM*$Yd4x->e`yr_*I)LXf^u R4+M9ED9EbFlt`Hb{tv#VEENC% literal 0 HcmV?d00001 diff --git a/04-массивы-ассоциативные-массивы-и-строки/images/image-4-1-4-3.png b/04-массивы-ассоциативные-массивы-и-строки/images/image-4-1-4-3.png new file mode 100644 index 0000000000000000000000000000000000000000..cd0193cf0360b91caa80656762a60de96a186287 GIT binary patch literal 15738 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 literal 0 HcmV?d00001 diff --git a/04-массивы-ассоциативные-массивы-и-строки/images/image-4-1-9-1.png b/04-массивы-ассоциативные-массивы-и-строки/images/image-4-1-9-1.png new file mode 100644 index 0000000000000000000000000000000000000000..c228e897b6ea1686bc3ed6211b7ec55834a1939e GIT binary patch literal 30530 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