Начинающие программисты нередко спотыкаются уже на первых строках кода: непонятно, зачем объявлять переменные вручную, если в Python или C компилятор сам разбирается с размерами. Ассемблер так не работает — здесь каждый участок памяти выделяется явно и точно.
Разобраться с этим помогают три директивы: DB, DW и DD. Материал пригодится тем, кто пишет программы на ассемблере или изучает архитектуру x86 — от первокурсника до разработчика встраиваемых систем.
По данным учебно-методического портала Intel Software Developer Resources, работа с директивами резервирования данных составляет базу любой программы на ассемблере: без корректного объявления переменных невозможно ни разместить, ни прочитать данные в нужном месте.
Типы данных в Ассемблере: зачем они нужны
В высокоуровневых языках размер переменной — забота компилятора. В ассемблере этот вопрос целиком лежит на программисте: транслятору нужно явно указать, сколько байт зарезервировать в сегменте данных и как интерпретировать записанное значение.
Никакой автоматики нет — если директива задаёт один размер, а команда ожидает другой, транслятор либо прервёт сборку с ошибкой, либо просто обрежет лишние биты без предупреждения.
На практике процессор x86 работает с тремя базовыми единицами: байт (8 бит), слово (16 бит) и двойное слово (32 бит). Именно под них созданы директивы DB, DW и DD. Каждая из них не просто «хранит число» — она указывает транслятору размер области и смещение переменной внутри сегмента.
Каждой переменной, объявленной через эти директивы, транслятор автоматически присваивает три характеристики: начальный адрес сегмента, смещение в байтах от его начала и тип — то есть объём выделенной памяти.
Располагая этими сведениями, программист точно знает, где находится нужная переменная, и может обратиться к ней из любого места кода без риска попасть в чужую область данных.
Смещение переменной в сегменте данных вычисляется от нулевого адреса сегмента. Чем раньше объявлена переменная — тем меньше её offset.
DB в ассемблере: однобайтовые переменные
DB (Define Byte) резервирует ровно один байт — 8 бит. Диапазон беззнакового значения: 0…255; со знаком: –128…127. Директива применяется для хранения символов, флагов, коротких счётчиков и элементов строк.
Синтаксис прост:
имя DB значение
Несколько примеров из реального кода (MASM / TASM):

Если значение неизвестно заранее — вместо числа ставят знак вопроса (DB ?). Тогда под переменную просто выделяется байт без инициализации.
Строки тоже объявляют через DB: каждый символ занимает отдельный байт. Признак конца строки — нулевой байт (0) или символы 13, 10 (перевод строки). Например: msg DB ‘Hello’, 13, 10, 0.
Директива DUP ускоряет объявление массивов: вместо пятидесяти нулей пишут DB 50 DUP(0). Синтаксис: количество DUP(значение).
DW в ассемблере: двухбайтовые переменные
DW (Define Word) выделяет двухбайтовое слово — 16 бит. Беззнаковый диапазон: 0…65 535; знаковый: –32 768…32 767. Это стандартный размер для коротких целых чисел, смещений в 16-битных сегментах и счётчиков циклов в старых программах.
Примеры:

Когда DW используется как операнд с символическим именем другой переменной, транслятор записывает в память только смещение (16-битный offset). Полный адрес (сегмент + смещение) можно получить лишь через DD. Этот нюанс важен при работе с указателями — путаница здесь приводит к доступу не по тому адресу.
Ещё одна особенность: ассемблер хранит многобайтовые числа в формате little-endian — младший байт первым. Значение 1000h (4096) для DW запишется в память как 00 10, а не 10 00. Это поведение стандартно для x86 и принципиально при разборе дампов памяти.
❗Путать DB и DW — одна из самых частых ошибок новичков. Если объявить переменную как DB, а потом загрузить её командой MOV AX, ассемблер выдаст ошибку типа несоответствия операндов.
DD в ассемблере: четырёхбайтовые переменные
DD (Define Double Word) резервирует четыре байта — 32 бита. Беззнаковый диапазон: 0…4 294 967 295; знаковый: –2 147 483 648…2 147 483 647. Это рабочий размер для 32-битных целых, адресов памяти и указателей в защищённом режиме x86.
Примеры объявления:

Ключевое отличие DD от DW: при хранении адреса переменной DD записывает в память полный 32-битный адрес (или пару сегмент:смещение в 16-битном режиме). Именно поэтому в 32-битных программах под MASM32 указатели почти всегда объявляются через DD.
В современных компиляторах DD соответствует типу dword (MASM) или dd (NASM). Регистр букв не важен: DD, dd и Dd — одно и то же. Транслятор воспринимает их идентично.
Порядок байтов: почему данные хранятся «задом наперёд»
Когда вы записываете двух- или четырёхбайтовое значение в память, ассемблер x86 не кладёт байты в привычном «человеческом» порядке — от старшего к младшему. Архитектура Intel использует формат little-endian: первым в памяти стоит младший байт, последним — старший.
Разберём на конкретном числе. Допустим, вы объявляете переменную:
value DW 0x1234
В памяти она займёт два байта по смещениям, скажем, 0000h и 0001h. Вы можете ожидать последовательность 12 34 — старший байт сначала. Но на деле сегмент данных хранит её как 34 12: сначала 0x34 (младший), потом 0x12 (старший). Для DD картина та же — значение 0x12345678 ляжет в память как 78 56 34 12.
На первый взгляд это выглядит как неудобство. Но для процессора всё ровно наоборот: он читает число, начиная с адреса переменной, и первым забирает тот байт, что вносит наименьший вклад в итоговое значение. Это упрощает аппаратную логику сложения и инкремента — операции, которые всегда начинаются с младших разрядов.
На практике это не создаёт проблем, пока вы работаете с переменной целиком — загружаете её в регистр командой MOV и обрабатываете стандартными инструкциями. Трудности начинаются, когда вы вручную читаете дамп памяти или передаёте данные по сети в формате big-endian (сетевой порядок байтов — прямой). В таких случаях нужна явная перестановка байтов: в x86 для этого существует инструкция XCHG и более удобная BSWAP (для 32-битных данных).
Понимание little-endian особенно важно при отладке: если в дампе видите байты 78 56 34 12 — это не «мусор», а корректно сохранённое значение 0x12345678. Начинающие нередко принимают такую запись за повреждение данных и тратят часы на поиск несуществующей ошибки.
Как выбрать директиву: DB, DW или DD
Выбор директивы определяется диапазоном значений и тем, с каким регистром вы будете работать. Ниже — сводная таблица трёх основных директив:
| Директива | Расшифровка | Размер | Диапазон (беззнак.) |
|---|---|---|---|
| DB | Define Byte | 1 байт (8 бит) | 0 … 255 |
| DW | Define Word | 2 байта (16 бит) | 0 … 65 535 |
| DD | Define Double Word | 4 байта (32 бит) | 0 … 4 294 967 295 |
Практическое правило: смотрите на регистр назначения. Команда MOV AL, val ожидает байт (AL — 8-битный). MOV AX, val — слово (AX — 16 бит). MOV EAX, val — двойное слово (EAX — 32 бит). Если размер переменной не совпадает с размером регистра, ассемблер сообщит об ошибке.
Второй ориентир — семантика данных:
- Символы и строки → DB.
- Короткие счётчики, смещения в 16-битном коде → DW.
- 32-битные числа, адреса в защищённом режиме → DD.
Для ещё больших значений есть DQ (8 байт) и DT (10 байт), но они нужны значительно реже — в основном при работе с числами с плавающей точкой и 64-битными операндами. Директивы DB DW DD в ассемблере покрывают подавляющее большинство практических задач.
✅Если сомневаетесь между DW и DD — выбирайте DD. Лучше потратить лишние 2 байта памяти, чем получить переполнение при записи значения больше 65 535.
Инициализация переменных в сегменте данных
Переменные в ассемблере объявляют в специальном сегменте данных — секции .DATA (MASM) или section .data (NASM). Именно здесь транслятор выделяет память и выполняет инициализацию начальными значениями.
Минимальный пример сегмента данных на MASM:

Переменные размещаются в памяти последовательно, одна за другой, точно в том порядке, в котором они объявлены. Первая переменная получает смещение 0000h, следующая — смещение, равное размеру предыдущей, и так далее.
Неинициализированная память
Знак ? вместо значения говорит транслятору: зарезервируй память, но ничего туда не пиши. Такое объявление допустимо для всех трёх директив: DB ?, DW ?, DD ?.
Реальное содержимое ячейки при старте программы непредсказуемо — это «мусор» из предыдущих данных. Поэтому если переменная должна начинать работу с конкретным значением, всегда задавайте его явно.
Массивы и оператор DUP
Для объявления массивов используют перечисление значений через запятую или оператор DUP:

Обращение к элементу массива — через смещение. Если marks начинается с offset 0, то третий элемент (значение 30) лежит по адресу marks + 2, так как каждый элемент занимает один байт. Для DW шаг будет 2 байта, для DD — 4.
В MASM переменные из секции .DATA? (неинициализированные) не попадают в исполняемый файл — они создаются в памяти только при запуске. Это уменьшает размер .exe.
Как ассемблер связывает имя переменной с адресом
Когда транслятор встречает объявление вроде counter DW 0, он не просто выделяет два байта. Он вносит имя counter в таблицу символов и фиксирует два параметра: номер сегмента и смещение внутри него. С этого момента каждый раз, когда имя counter встречается в коде, транслятор подставляет вместо него конкретный адрес — программист имени уже не видит, процессор работает только с числами.
Это принципиально отличается от языков высокого уровня, где переменная — абстракция с именем, типом и областью видимости. В ассемблере имя существует только на этапе трансляции. После сборки программы в исполняемый файл от символических имён не остаётся ничего — только адреса и значения в памяти. Именно поэтому отладка ассемблерного кода без специальных средств требует умения читать дампы и знания смещений вручную.
Смещение переменной внутри сегмента данных можно узнать оператором OFFSET. Выражение OFFSET counter вернёт числовое смещение переменной от начала сегмента — это полезно, когда нужно передать адрес переменной в регистр или сохранить его в другой переменной через DD. Полный сегментный адрес (пара сегмент:смещение) формируется уже на этапе загрузки программы операционной системой.
Важный практический момент: две переменные, объявленные подряд в секции .DATA, гарантированно лежат в памяти вплотную друг к другу. Между ними нет никаких промежутков, если вы сами их не создали. Это позволяет строить простые структуры данных — например, записи фиксированной длины — просто располагая поля одно за другим и обращаясь к ним через смещение от имени первого поля.
Подытожим
Директивы DB, DW и DD — это фундамент работы с данными в ассемблере x86. Каждая задаёт размер резервируемой области: байт, слово или двойное слово. Правильный выбор директивы исключает ошибки переполнения и несоответствия типов операндов.
Все три директивы работают по одному принципу: имя переменной → директива → значение (или ? для отложенной инициализации). Переменные последовательно размещаются в сегменте данных, получают смещение и становятся доступны по имени из любого места программы.
Когда разберётесь с базовыми директивами, следующий шаг — изучение адресации через смещение и работа с массивами. Это позволяет уже строить полноценные структуры данных прямо на уровне памяти — без лишних абстракций.
Вам нужна биржа фриланса для новичков или требуются разработчики сайтов?


Комментарии