DLL - это сокращение от Dynamic Link Library (динамически загружаемая библиотека).

http://sg.uploads.ru/t/lCK2D.png

С формальной точки зрения DLL - особым образом оформленный относительно независимый блок исполняемого кода.
Особый способ оформления предполагает наличие в DLL так называемых секций импорта и экспорта. Секция экспорта указывает те идентификаторы объектов (функций, классов, переменных), доступ к которым предоставляет данная DLL. В этом случае мы говорим об экспортировании идентификаторов из DLL. В общем случае, именно секция экспорта предоставляет особый интерес для разработчиков. Хотя ничто не мешает реализовать DLL, которая не имеет данной секции, но, тем не менее, выполняет полезную работу.
Относительная независимость связана с наличием/отсутствием секции импорта у DLL (т.е. секции, в которой описываются внешние зависимости данной DLL от других). Подавляющее большинство DLL (за исключением, быть может, DLL ресурсов) импортирует функции из системных DLL (kernel32.dll, user32.dll, gdi32.dll и др.). В большинстве случае при создании проекта в его опциях автоматически проставляется стандартный набор таких библиотек. Иногда в этот список необходимо добавить требующиеся для Ваших задач DLL (например, в случае использования библиотеки сокетов требуется дополнительно подключить библиотеку ws2_32.dll).
"Исполняемый" код в DLL не предполагает автономного использования. Перед тем, как можно будет приступить к использованию, необходимо загрузить DLL в область памяти вызывающего процесса (т.е. DLL не может выполняться сама по себе - ей обязательно нужен клиент). Это явление носит название "проецирование DLL на адресное пространство процесса". И это не удивительно, если вспомнить тот факт, что процессор работает не только с регистрами, но и с адресами памяти. Поэтому каждому объекту DLL требуется свое место "под солнцем", чтобы иметь возможность быть выполненным при вызове. В конечном коде exe-файла, который генерирует компилятор, не будет инструкций процессора, соответствующих коду данной функции. Вместо этого будет сгенерирована инструкция вызова соответствующей функции (call). Так как DLL отображена на адресное пространство процесса, то код DLL будет легко доступен по call-вызову.
Итак, формально, DLL - особым образом оформленный программный компонент, доступ к исполняемому коду которого приложение получает в момент старта (DLL неявной загрузки) или в момент использования (DLL явной и отложенной загрузки).

Что же касается физического представления на диске, то разница между dll- и exe-файлами небольшая. Как динамически линкуемые библиотеки, так и исполняемые модули приложений в Windows имеют формат Portable Executable (PE-файл), однако вы не можете "запустить" DLL-библиотеку на выполнение, как обычное приложение. "Узнать" DLL-файл можно по его сигнатуре (заголовку): признаком библиотеки является установленный флаг IMAGE_FILE_DLL (13-й бит) в поле Characteristics заголовка IMAGE_FILE_HEADER (эти константы описаны в файле winnt.h; подробная информация о формате exe- и dll-файлов - см. MSDN "Microsoft Portable Executable and Common Object File Format Specification"). Кроме того, в заголовках файлов динамически линкуемых библиотек указан нулевой размер стека - это связано с тем, что функции DLL используют стек вызывающего приложения. DLL-файл может содержать как инструкции (команды процессора), так и данные (разделяемые ресурсы).
В общем случае, файл, являющийся динамически загружаемой библиотекой, не обязан иметь расширение .dll. Например, известные файлы *.cpl - это не что иное, как DLL, используемые апплетом панели управления; *.ocx - DLL, содержащие внутрипроцессные (inproc) COM-объекты.

Логическое (философское) представление DLL не имеет никаких ограничений. Удобно представлять себе DLL в виде сервера, который предлагает дополнительную функциональность Вашему приложению. Приложения, которые используют данную функциональность, являются клиентами DLL. Рисунок 1.1 показывает процесс взаимодействия приложения с DLL. После проецирования DLL на адресное пространство вызывающего процесса DLL становится частью этого процесса. Поэтому возможен абсолютно безболезненный вызов функций, экспортируемых DLL.

Зачем нужны DLL?

Предпосылки к появлению DLL

Свою историю DLL ведут с средины 60-х годов прошлого столетия, однако по-настоящему широкое распространение динамически линкуемые библиотеки получили после появления операционной системы Windows.
По крайней мере, доподлинно известно, что операционные системы Windows 3.1/3.11 уже содержали программы, использующие DLL. В связи с тем, что в те времена емкости оперативной памяти и жесткого диска были значительно меньше, чем сейчас, использование DLL предоставляло ряд преимуществ:

экономия дискового пространства за счет многократного использования кода (reusing). Если приложения используют один и тот же код, нет необходимости поставлять его в коде каждого приложения. Достаточно разработать DLL.
экономия физической памяти (RAM) за счет загрузки в нее единственного экземпляра DLL. Именно тогда появились счетчики ссылок пользователей DLL - при каждом вызове функции ОС проверяет наличие загруженного в память экземпляра библиотеки. В случае положительного ответа счетчик ссылок пользователей данной DLL увеличивается на единицу. Если же экземпляр данной DLL в памяти не обнаружен, то операционная система загружает файл в память и присваивает счетчику значение "1". Механизм разделения кода носит название memory mapping (отображение в память). При выгрузке DLL из памяти уменьшается значение счетчика числа пользователей, в случае равенства его нулю DLL немедленно выгружается.
изолирование и модификация кода DLL независимо от остального кода программы. Например, код визуализации изолируется от математической части. При изменении математического аппарата (например, при разработке нового, более быстрого алгоритма) перекомпиляция кода клиентского приложения (отвечающего за визуализацию результатов) не требуется. Этот фактор может играть значительную роль в том случае, если число клиентов достаточно велико.

Самое удивительное (время не стоит на месте!), что ранее политика Microsoft позволяла (и даже приветствовала) размещение DLL в системных директориях (таких как WindowsSystem, WindowsSystem32). Это порождало периодические проблемы конфликта версий при замене DLL (см. раздел "DLL Hell"). В связи с этим (а также с ростом емкости запоминающих устройств и соответствующим снижением цены на них) на данный момент политика Microsoft  изменена на прямо противоположную - Microsoft настоятельно рекомендует хранить все используемые DLL в рабочем каталоге программы и лишь в случае острой необходимости пользоваться системными директориями.

Зачем нужны DLL сейчас?

Основные направления использования DLL:

всевозможные модули расширения функциональности приложений - так называемые plug-in (см. пример с MatLab, Far и пр.);
локализация приложения (подробнее об этом в разделе "DLL, содержащие только ресурсы");
разделение объектов абстракции (функций, классов и пр.) между приложениями;
независимость модификации кода - DLL может быть в любой момент переписана с сохранением экспортируемых интерфейсов;
реализация определенных действий, которые можно совершить только при помощи DLL - см. раздел "Перехват API-вызовов";
хранилище ресурсов с возможностью независимого изменения этих ресурсов.

Кстати, DLL широко используются в технологии COM (а до этого в OLE 1.0) - в качестве основы при построении так называемых inproc-серверов (внутрипроцессных серверов) используются DLL. Это позволяет упростить взаимодействие с приложением, благодаря загрузке используемых ActiveX объектов в адресное пространство клиента. В этом случае накладные расходы, связанные с преодолением границ адресных пространств при передаче данных (параметров функций и т.д.) - так называемый marshalling, сводятся к нулю.
Все те абстракции, с которыми Вы работаете в повседневной программистской жизни, могут быть внедрены в DLL - классы, объекты, таймеры, потоки, функции и пр. Другое дело, что не всегда удобно и правильно работать с этими объектами вне DLL (см. например "Использование STL в DLL"). Связано это с тем, что не всегда логическое представление того или иного объекта может быть однозначно представлено (переведено) в физическое. Да-да, Вы не ослышались - использование DLL не налагает ограничений на используемый язык (точнее - почти не налагает!). Более того, как правило, DLL разрабатывается на другом языке программирования, нежели тот, который используется при ее загрузке. Приведем пример: разрабатывался математический проект, код которого реализовывался на M-языке среды MatLab (Matrix Laboratory). M-язык по своей природе является интерпретируемым языком программирования. После этого полученные алгоритмы были реализованы при помощи языка C++ и скомпилированы в DLL, которая также использовалась средой MatLab.

Что лучше: один EXE-файл и никаких DLL или компактный EXE-файл и много DLL?

Вероятно известный вам Бьерн Страуструп в своей книге "Язык программирования C++" дает после каждой главы ряд полезных советов, один из которых звучит так: "Пользуйтесь здравым смыслом...". Этот же совет можно предложить и при использовании DLL в программе.
Если вы считаете, что вам нужна DLL - сделайте ее, если нет других причин сделать иначе. Разумеется, не стоит каждую отдельную функцию размещать в DLL и затем экспортировать (хотя это и возможно). Со временем такой проект превратится в трудноуправляемого монстра, инициализация которого будет занимать порядочное время. Правда, экспортирование только одной функции из DLL может быть продиктовано следующей логикой: серверное приложение сканирует определенную директорию на предмет нахождения в ней файлов с определенным расширением. В случае нахождения такого файла сервер полагает, что это - DLL, которая экспортирует функцию с определенным именем (скажем, CplApplet или mexFunction), и, соответственно, он сможет ее загрузить. Таким простым образом возможно динамическое расширение функциональности приложения - именно такой подход проповедуется в случае Панели управления (Control Panel) и системы MatLab (Matrix Laboratory).