D (язык программирования)

Материал из Википедии — свободной энциклопедии
Перейти к: навигация, поиск
D
Семантика:

мультипарадигменный: императивное, объектно-ориентированное, контрактное[1], обобщённое программирование

Тип исполнения:

компилятор

Появился в:

2001

Автор(ы):

Уолтер Брайт

Релиз:

2.065.0 (2014-02-24; 51 день тому назад[2])

Тестовая версия:

2.065

Система типов:

строгая, статическая

Основные реализации:

Digital Mars D, LDC, GDC

Испытал влияние:

Си, C++, C#, Eiffel

Сайт:

dlang.org

D — объектно-ориентированный, императивный, мультипарадигмальный язык программирования, созданный Уолтером Брайтом из компании Digital Mars. Изначально был задуман как реинжиниринг языка C++, однако, несмотря на значительное влияние С++, не является его вариантом. В D были заново реализованы некоторые свойства C++, также язык испытал влияние концепций из других языков программирования, таких как Java, Python, Ruby, C# и Eiffel.

При создании языка D была сделана попытка соединить производительность компилируемых языков программирования с безопасностью и выразительностью динамических. Код на языке D обычно работает так же быстро как эквивалентный код на C++, при этом программа на D короче и обеспечивает безопасный доступ к памяти.[источник не указан 264 дня]

Стабильная версия компилятора 1.0 вышла 2 января 2007[3]. Экспериментальная версия компилятора 2.0 выпущена 17 июня 2007 года.[4]

Стабильная версия компилятора 1.0 работает на Windows, Linux, Mac OS, а с версии 1.043 также и на FreeBSD. Также для загрузки доступны исходные коды DMD (официальная реализация компилятора от Digital Mars)[5].


Dязык программирования общего назначения, предназначенный для прикладного и системного программирования. Разработан Уолтером Брайтом и его фирмой Digital Mars. D является языком высокого уровня, но сохраняет возможности прямого взаимодействия с программным интерфейсом операционной системы и с оборудованием[6]. D предназначен для написания средних и крупных систем с миллионами строк исходного кода, для ведения командной разработки. Язык D имеет C-подобный синтаксис, который более лаконичен и читабелен, чем синтаксис С++, для человека, знающего, например, С, C# или Java[7], поддерживает многие возможности в помощь программисту[8][9][10][11], а также пригоден для проведения неплохой оптимизации кода компилятором[12].

Содержание

Возможности, унаследованные от C++[править | править исходный текст]

Синтаксис языка D схож с синтаксисом C++ и C#, что облегчает его изучение людям, знакомым с этими языками, а также перенос исходного кода с С++.

Для D не существует виртуальной машины. Программы, написанные на D, компилируются в объектные файлы, которые затем могут быть объединены компоновщиком в исполняемый файл. Стандартные инструменты для разработки на С++ (например make) могут быть использованы для D.

Что отсутствует в языке D[править | править исходный текст]

  • Совместимость с исходным кодом на языке C. Уже существуют языки программирования, в полной мере или практически полностью совместимые с исходным кодом, написанным на языке C (Objective-C, C++ и Vala). Авторы посчитали, что дальнейшая работа в этом направлении препятствует реализации существенных возможностей. Однако они постарались не допускать нестандартного поведения заимствованых из C операторов, которое может приводить к трудно исправляемым ошибкам.
  • Препроцессор[13]. Включение файлов посредством #include заменено на модули с собственным пространством имён. Неструктурированные директивы типа #ifdef заменены на структурированные блоки version и debug.[14] Выполнение кода во время компиляции, включение любых константных выражений, а также чтение файлов во время компиляции могут в большинстве случаев эмулировать макро-язык C с большей надёжностью (тем не менее, некоторые возможности C утрачены, например, нельзя написать аналог #define TEXT(quotes) L ## quotes), а также открывают множество дополнительных возможностей, например перевод кода другого языка программирования в D «прямо во время компиляции».
  • Множественное наследование. Однако это частично компенсируется более надёжными интерфейсами, работа с которыми поддерживается языком D.
  • Пространства имён (namespaces). Пространства имён были попыткой решить проблему, возникающую при объединении разработанных независимо друг от друга кусков кода, когда пересекаются имена переменных, типов данных и так далее. Модульный подход выглядит проще и удобнее для использования.
  • Битовые поля (bit fields) произвольного размера. Битовые поля сложны, неэффективны и достаточно редко используются. Компенсируется модулем в стандартной библиотеке D 2.0[15]
  • Поддержка 16-битных компьютеров. В языке D нет никаких решений для генерирования качественного 16-битного кода.
  • Взаимная зависимость проходов компилирования (compiler passes). В языке C++ успешная обработка исходного кода основывается на таблице символов (symbol table) и различных командах препроцессора. Это делает невозможным предварительную обработку кода и значительно усложняет работу анализаторов кода.
  • Оператор разыменования с обращением к члену класса ->. В языке D оператор обращения к члену класса производит разыменование по умолчанию при необходимости.

Для чего предназначен язык D[править | править исходный текст]

  • Использование анализаторов кода;
  • Компиляция с максимальным количеством включенных уровней предупреждений (warning levels);
  • Упрощение и укрощение ООП C++;
  • Автоматическое управление памятью;
  • Встроенное тестирование и верификация;
  • Написание крупных приложений;
  • Встроенное автоматическое написание программ;
  • Абстрактная работа с указателями;
  • Программирование математики. Язык D поддерживает работу с комплексными числами и операторами определения NaN’ов (not a number) и бесконечностей (infinity). Они были добавлены в стандарт C99, но не были добавлены в C++;

Для чего D не предназначен[править | править исходный текст]

  • Для переноса на него в неизменном виде старого (legacy) кода на С/С++;
  • Скриптового программирования;

Особенности языка D[править | править исходный текст]

Объектно-ориентированное программирование[править | править исходный текст]

Классы[править | править исходный текст]

Объектно-ориентированная природа языка D происходит от классов. Модель наследования не поддерживает наследования от нескольких классов, зато расширяется за счет использования интерфейсов. На вершине иерархии наследования находится класс Object, от которого все классы наследуют базовый набор функциональности. Экземпляры классов работают по ссылке, поэтому после обработки исключений не требуется писать сложный код для очистки памяти.

Перегрузка операторов[править | править исходный текст]

Классы могут быть приспособлены для работы с уже существующими операторами. Например, можно создать класс для работы с большими числами и перегрузить операторы +, -, * и /, чтобы использовать алгебраические операции с этими числами.

Эффективность[править | править исходный текст]

Модули[править | править исходный текст]

Файлы исходного кода взаимно однозначно соответствуют модулям. Вместо включения (#include) файлов исходного кода достаточно импортировать модуль. В этом случае нет необходимости беспокоиться о том, что один и тот же модуль будет импортирован несколько раз, а, значит, и нет необходимости обрамлять код в заголовочных файлах с использованием макросов препроцессора #ifndef/#endif или #pragma once. Однако, стоит помнить, что статические конструкторы (static this() { ... }, вызывается один раз до функции main()[16]) всех импортированных (прямо или косвенно) модулей вызываются до статического конструктора модуля-импортёра, то есть возможны циклические зависимости, которые порой трудно устранить (например, что-то в модуле А зависит от модуля B, а в B — от А). Тогда инициализацию модулей приходится реализовывать вызовами функций в начале main().

Объявление против описания[править | править исходный текст]

C++ обычно требует, чтобы функции и классы были объявлены дважды — объявление происходит в заголовочных файлах (*.h), а описание происходит в файлах исходного кода (*.cpp). Это утомительный и подверженный ошибкам процесс. Очевидно, что программисту достаточно объявить функцию или класс лишь однажды, а компилятор должен впоследствии извлечь информацию об объявлении и сделать ее доступной для импортирования. Именно так работает язык программирования D, например:

class ABC
{
    int func() { return 7; }
    static int z = 7;
};
 
int q;

И более нет необходимости отдельного описания функций-членов, атрибутов и спецификаций внешних объявлений (extern), как в языке C++:

int ABC::func() { return 7; }
 
int ABC::z = 7;
 
extern int q;

Заметка: Конечно же, в C++ тривиальные функции вроде { return 7; } тоже могут быть описаны внутри класса, но более сложные рекомендуется определять вне класса. Вдобавок, если нужны опережающие ссылки (ссылки на класс или функцию, которые объявлены, но ещё не определены), то для этих объектов нужны прототипы (prototype). Следующий код не будет работать в C++:

class Foo
{
    int foo(Bar *c) { return c->bar(); }
};
 
class Bar
{
    public:
        int bar() { return 3; }
};

Но эквивалентный код на языке D будет рабочим:

class Foo
{
    int foo(Bar c) { return c.bar; }
}
 
class Bar
{
    int bar() { return 3; }
}

А то, будет ли функция встраиваемой (при компиляции вызов такой функции заменяется ее кодом) или нет, в языке D зависит от настроек оптимизатора.

Шаблоны[править | править исходный текст]

Шаблоны в языке D предлагают простой путь поддержки обобщенного программирования, в то же время обеспечивая использование частичной специализации.

Ассоциативные массивы[править | править исходный текст]

Ассоциативные массивы — это массивы, в качестве индекса в которых можно использовать любой тип данных, а не только целые числа. По существу, ассоциативные массивы являются хеш-таблицами, которые облегчают создание быстрых, эффективных и устойчивых к ошибкам таблиц символов.

Настоящий typedef[править | править исходный текст]

В языках C и C++ оператор typedef на самом деле просто создает синоним типа данных и никакого нового типа данных не объявляется. В языке D оператор typedef объявляет новый тип данных. Таким образом, код

typedef int handle;

создает новый тип данных handle. К новому типу данных применяется проверка на соответствие типу данных, а также при перегрузке функций новый тип данных отличается от того типа данных, на основе которого он был создан. Например:

int foo(int i);
int foo(handle h);

Для того, чтобы создать псевдоним для типа, аналогично оператору typedef языка C, используется ключевое слово alias:

alias int sint32;

Начиная с версии 2.057, использование typedef не рекомендуется.[17]. Вместо него следует использовать шаблон std.typecons.Typedef.

Документация[править | править исходный текст]

Документирование традиционно происходит в два этапа — написание комментариев, описывающих, что делают функции, а затем перенос этих комментариев вручную в отдельные HTML-файлы и файлы документации man. И, естественно, со временем то, что описывается в документации, будет отличаться от того, что на самом деле происходит в исходном коде. Возможность генерации документации напрямую из комментариев в файлах исходного кода не только экономит время при подготовке документации, но и облегчает поддержку соответствия документации исходному коду. Для языка D существует единая спецификация для генератора документации.

Для генерации документации в языке C++ существуют инструментальные средства, разрабатываемые отдельно от компиляторов. Использование этих инструментальных средств имеет ряд недостатков:

  • Очень сложно обработать C++ код на 100 % правильно, для этого на самом деле потребуется компилятор. Сторонние инструменты (third party tools) корректно работают только с подмножеством языка C++.
  • Разные компиляторы поддерживают разные версии языка C++ и разные расширения языка. Очень трудно соответствовать всем этим вариациям поддержки языка C++.
  • Инструменты для генерирования документации из C++ могут не быть реализованы для некоторых платформ или могут не соответствовать последней версии компилятора.

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

Функции[править | править исходный текст]

Язык D имеет поддержку обычных функций, включая глобальные функции, перегруженные функции, встраивание функций, методы классов, виртуальные методы классов, указатели на функции. Также в D реализованы:

Вложенные функции[править | править исходный текст]

Функции могут быть объявлены в теле других функций, что очень полезно для ограничения области видимости вспомогательных функций.

Функциональные литералы (function literals)[править | править исходный текст]

Анонимные функции могут быть встроены напрямую в выражение.

Динамические делегирования (closure)[править | править исходный текст]

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

Спецификаторы доступа к параметрам функций: in, out и inout[править | править исходный текст]

Данные спецификаторы не только делают объявления функций говорящими сами за себя, но и открывает возможности компилятора для помощи в нахождении ошибок.

Все эти возможности D позволяют использовать больше различных программных интерфейсов, а также избавляет от необходимости использовать различные искусственные приемы, как, например IDL (Interface Definition Languages).

Неизменяемые данные[править | править исходный текст]

В языке D второй версии имеется два ключевых слова для описания неизменяемости: const и immutable.

immutable применяется к данным, которые не могут изменяться. Неизменяемые данные с момента создания и до завершения программы не меняют своё значение. Неизменяемые данные могут физически располагаться на ПЗУ или защищённых от записи страницах памяти. Неизменяемость данных открывает широкие возможности оптимизации, в частности, неизменяемые данные неявно разделяются между потоками.

const применяется к данным, которые нельзя изменить, используя константную ссылку на эти данные. Сами данные при этом могут быть изменяемыми (с использованием другой ссылки). Используется при описании программных интерфейсов, которые обещают не изменять данные.

Данные, не помеченные ни одним из этих ключевых слов, являются изменяемыми.

Особенностью этих квалификаторов в D является их транзитивность — любые данные, доступные через неизменяемую(константную) ссылку, будут также неизменяемыми(константными):

immutable char[] s = "foo";
s[0] = 'a';  // ошибка, s ссылается на неизменяемые данные
s = "bar";   // ошибка, s неизменяемо

Изменяемые и неизменяемые ссылки неявно приводимы к константным ссылкам.

Передача неизменяемости[править | править исходный текст]

Массивы[править | править исходный текст]

Массивы в языке C имеют несколько недостатков, которые приходится корректировать:

  • Информация о размерности массива не прилагается к массиву, поэтому должна извлекаться дополнительно и передаваться отдельно. Классический пример — это объявление функции main(int argc, char *argv[]). В языке D, функция main объявляется так: main(char[][] args);
  • Когда массив передается в функцию, то он неявно преобразуется в указатель на первый элемент массива, даже когда прототип функции говорит, что должен быть передан массив. При этом информация о размере массива теряется. (Однако в С++ при передаче массива по ссылке (а не по значению) информация не теряется).
  • Размерность массивов языка C не может быть изменена. Это означает, что даже такая простая структура данных, как стек, должна быть реализована в виде сложного класса;
  • В языке C нельзя произвести проверку на нарушение границ массива, просто потому, что размерность массива неизвестна (однако ее реализация сильно замедляет работу программы);
  • Массивы объявляются с использованием оператора [] после названия массива. Это ведет к использованию очень неуклюжего синтаксиса при объявлении, скажем, ссылки (указателя — в терминах C) на массив:
int (*array)[3];

В языке D оператор [] при объявлении массива может ставиться после типа данных:

int[3]* array; // объявляется ссылка на массив из трёх целых чисел
long[] func(int x); // объявляется функция, возвращающая массив длинных целых

Этот код более легок для восприятия. В языке D можно использовать несколько видов массивов: указатели, статические массивы, динамические массивы и ассоциативные массивы.

Статические массивы[править | править исходный текст]

int[3] array;

Статические массивы имеют длину, заданную на этапе компиляции. В языке D2, в отличие от D1 и C статические массивы используют семантику значения, так же как структуры и примитивные типы.

Динамические массивы[править | править исходный текст]

int[] array;
array = new int[20];

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

Ассоциативные массивы[править | править исходный текст]

int[string] b;

Ассоциативные массивы — это массивы, в качестве индекса в которых можно использовать любой тип данных, а не только целые числа. Они также используют семантику ссылки. Внутренняя реализация ассоциативных массивов зависит от используемого компилятора. Для работы с ассоциативными массивами, помимо индексирования, используется оператор in, который возвращает указатель на элемент с указанным ключом, если он существует, или нулевой указатель в противном случае:

int* p;
p = ("hello" in b); 
if (p !is null)
{
    //Работа с p
}

Строки[править | править исходный текст]

Работа со строками нужна в большинстве современных программ, так или иначе работающих с текстом. Поддержка строк в языке С — весьма низкоуровневая, а C++ сохраняет совместимость с ним, добавляя более продвинутые механизмы работы со строками, как специальными контейнерами данных в стандартной библиотеке STL. Некоторые из популярных библиотек, например, Qt, добавляют свои типы данных для хранения строк (напр. для лучшей поддержки юникода). Поэтому, при программировании на С++ бывают ситуации, когда программисту приходится одновременно работать со строками 2-х — 3-х типов. Чтобы избежать этой ситуации — работа со строками в языке D пересмотрена.

Управление ресурсами (resource management)[править | править исходный текст]

Автоматическое управление памятью[править | править исходный текст]

Выделение памяти в языке D полностью контролируется методикой «сборки мусора». Опыт показывает, что большинство сложных возможностей языка C++ требуют последующего освобождения памяти. Методика «сборки мусора» делает жизнь проще. Существует мнение, что механизм «сборки мусора» нужен только ленивым и начинающим программистам. В конце концов, в C++ нет ничего такого, чего нельзя было бы сделать в C или в ассемблере. «Сборка мусора» избавляет от утомительного написания кода, отслеживающего процесс выделения памяти, который, к тому же, может быть подвержен появлению ошибок. Конечно, «сборку мусора» можно использовать и в C++, но этот язык не дружественен по отношению к «сборщикам мусора», что не может не сказаться на эффективности «сборки мусора».

Явное управление памятью[править | править исходный текст]

Несмотря на то, что язык D поддерживает автоматическую сборку мусора, операторы new и delete могут быть перегружены для определенных классов.

RAII[править | править исходный текст]

RAII — это современная методика разработки для управления распределением и освобождением ресурсов. Язык D поддерживает методику RAII в контролируемой и предсказуемой манере, независимой от цикла сборки мусора.

Производительность[править | править исходный текст]

Легковесные составные типы данных[править | править исходный текст]

Язык D поддерживает простые структуры в стиле языка C не только для совместимости с этим языком, но и потому что они очень полезны в тех случаях, когда возможностей классов слишком много.

Встроенный ассемблер[править | править исходный текст]

Драйверы устройств, высокопроизводительные системные приложения, а также встраиваемые системы (embedded systems) требуют углубления до уровня команд ассемблера. Программирование на языке D не требует использования ассемблера, но он реализован и является частью языка.

Отладочный код[править | править исходный текст]

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

Обработка исключений[править | править исходный текст]

Модель try-catch-finally предпочтительнее, чем просто try-catch, потому что не требует создания временных (dummy) объектов, деструктор которого будет выполнять то, что может сделать finally.

Синхронизация[править | править исходный текст]

Многопоточное программирование становится всё более распространенным, поэтому в языке D реализованы базовые возможности для создания многопоточных приложений. Синхронизация может быть применена ко всему объекту или к отдельным его методам.

synchronized int func() { ... }

Синхронизированные функции разрешают в один момент времени исполнять код функции только одному потоку.

Устойчивые к ошибкам методики

  • Динамические массивы вместо указателей;
  • Переменные-ссылки вместо указателей;
  • Ссылки на объекты вместо указателей;
  • Сборка мусора вместо явного управления памятью;
  • Встроенные возможности синхронизации потоков;
  • Встраиваемые функции вместо макросов;
  • Уменьшение необходимости использовать указатели;
  • Явные размеры целого типа данных;
  • Отсутствие неопределенности, касающейся наличия знака у символьного типа;
  • Отсутствие необходимости повторного объявления;

Проверки во время компиляции

  • Более строгая проверка на соответствие типа данных;
  • Никаких пустых условий в цикле for;
  • Присвоения не возвращают булевого значения;
  • Проверка использования устаревших API;

Проверки во время выполнения

  • Выражения assert();
  • Проверка на выход за пределы массива;
  • Исключение при нехватке памяти;

Совместимость[править | править исходный текст]

Приоритеты операторов и правила вычисления[править | править исходный текст]

Язык D сохранил операторы из языка C, а также их приоритеты и правила вычисления. Это позволит избежать ситуаций, когда операторы ведут себя неожиданным образом (тем не менее Кёрниган и Ритчи, авторы языка C, признаю́т, что где-то приоритет операторов в этом языке нелогичен[18]).

Прямой доступ к API языка C[править | править исходный текст]

Язык D не только имеет типы данных, соответствующие типам данных языка C, но и обеспечивает прямой доступ к функциям языка C. В таком случае нет необходимости писать функции-обертки (wrapper functions) или копировать значения членов составных типов по одному.

Поддержка всех типов данных языка C[править | править исходный текст]

Это делает возможным взаимодействие с API языка C или с кодом существующей библиотеки языка C. Эта поддержка включает структуры, объединения, перечисления, указатели и все типы данных, введённые в стандарте C99.

Обработка исключений операционной системы[править | править исходный текст]

Механизм обработки исключений языка подключается к механизмам обработки исключений операционной системы.

Использование существующих инструментариев[править | править исходный текст]

Код на языке D преобразуется в объектный файл стандартного формата, что делает возможным использование стандартных ассемблеров, компоновщиков, отладчиков, профайлеров и компрессоров исполняемых файлов.

Управление проектом[править | править исходный текст]

Контроль версий[править | править исходный текст]

В языке D реализована встроенная поддержка генерирования нескольких версий программ из одного исходного кода. Это заменяет использование команд препроцессора #if и #endif.

Устаревание[править | править исходный текст]

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

Отсутствие предупреждений (warnings)[править | править исходный текст]

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

Отсутствие макросов[править | править исходный текст]

Отказ создателей языка от препроцессора (как, например, в языке Си) многие расценивают как рискованный и неверный шаг. Но в D имеются встроенные средства, которые позволяют обходиться без препроцессора. По мнению многих, препроцессор C чересчур мощный и опасный.

Реализация на языке Си Реализация на языке D
#if !defined(SOME_FILE_H)
//#ifndef SOME_FILE_H
#define SOME_FILE_H
// Код
#endif
// Объявление постоянной
#define MAX_INT 32767
 
// Создание псевдонимов
#define true TRUE
 
#if defined(Mac_OS_X)
// Код для Mac
#else
// Иначе
#endif
// Объявление постоянной
enum uint MAX_INT=32767
 
// Создание псевдонимов
alias TRUE true
 
version(Mac OS X)
{
    // Код для Mac
} else {
    // Иначе
}

Стандартная библиотека[править | править исходный текст]

Стандартная библиотека языка D носит название Phobos.

В первой версии языка D было две широко используемых стандартные библиотеки: официальная Phobos и альтернативная Tango. Данные библиотеки в то время были несовместимы, так как каждая предоставляла свою реализацию сборщика мусора, многопоточности и других функций.

Существование двух разных библиотек создавало некоторые трудности. Для написания кода, работающего с обеими библиотеками, приходилось использовать конструкции наподобие следующей:

version (Tango)
{
    // Код, работающий с функциями Tango
} else
{
    // Код, работающий с функциями Phobos
}

Во второй версии библиотека поддержки времени исполнения была вынесена в отдельный модуль. Теперь можно совместно использовать Phobos и Tango.

Tangobos[править | править исходный текст]

Для того чтобы решить проблемы с библиотеками, был запущен проект Tangobos. Tangobosобёртка Tango, дающая программисту интерфейс Phobos.

Примеры[править | править исходный текст]

Эта программа печатает аргументы командной строки. Функция main является точкой входа программы, а args — массив с параметрами запуска программы.

module main; // Желательно объявлять
 
version (Tango)
    import tango.io.Stdout;
else
    import std.stdio; // для writefln()
 
void main(char[][] args)
{
    foreach(int i, char[] a; args)
    {
        version(Tango)
            Stdout.formatln("args[{}] = {}", i, a);
        else
            writefln("args[%d] = '%s'", i, a);
    }
}

См. также[править | править исходный текст]

Примечания[править | править исходный текст]

  1. Описание реализации парадигмы контрактного программирования в языке Ди.
  2. Список изменений в компиляторе языка Ди. Архивировано из первоисточника 1 июня 2012.
  3. DMD 1.00 — here it is! (digitalmars.D.announce) (англ.)
  4. D 2.0 changelog. Проверено 11 января 2009. Архивировано из первоисточника 1 июня 2012. (англ.)
  5. Исторический момент включения исходных текстов в dmd 1.041 (англ.)
  6. D x86 Inline Assembler Встроенный ассемблер в D
  7. [1] Пример программы на D (англ.)
  8. [2] Модульное тестирование D программы (англ.)
  9. [3] Контрактное программирование в D (англ.)
  10. Embedded Documentation Встроенный генератор документации языка D (англ.)
  11. [4] dmd может копмилировать программы из html файла (англ.)
  12. Named Return Value Optimization Личная техника оптимизации Уолтера Брайта (англ.)
  13. The C Preprocessor Versus D — digitalmars.com (англ.)
  14. Conditional Compilation — digitalmars.com (англ.)
  15. std.bitmanip (англ.)
  16. [5] Статические конструкторы в D
  17. Deprecated features (англ.)
  18. [Яык программирования C. Второе издание. Введение: Как и у любых языков, у C есть свои недостатки: некоторые операции имеют нелогичный приоритет <…>]

Ссылки[править | править исходный текст]